package com.xiaoshuidi.cloud.module.contract.service.contract.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xiaoshuidi.cloud.framework.common.exception.ServiceException;
import com.xiaoshuidi.cloud.framework.common.pojo.CommonResult;
import com.xiaoshuidi.cloud.framework.common.pojo.PageParam;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.framework.common.util.json.JsonUtils;
import com.xiaoshuidi.cloud.framework.mybatis.core.util.MyBatisUtils;
import com.xiaoshuidi.cloud.framework.redis.lock.DistributeLock;
import com.xiaoshuidi.cloud.framework.redis.sequence.TimebaseSequenceGenerator;
import com.xiaoshuidi.cloud.framework.security.core.LoginUser;
import com.xiaoshuidi.cloud.framework.security.core.util.SecurityFrameworkUtils;
import com.xiaoshuidi.cloud.framework.systemevent.core.LeaseSystemEventProducer;
import com.xiaoshuidi.cloud.framework.systemevent.core.data.LeaseSystemEventNewSign;
import com.xiaoshuidi.cloud.module.bpm.api.task.BpmProcessInstanceApi;
import com.xiaoshuidi.cloud.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import com.xiaoshuidi.cloud.module.bpm.enums.BpmBusinessTypeEnum;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.DateRangeGenerator;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.OrderDetailVo;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.OrderDetaillAndOrderFeeVo;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.OrderResp;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.orderFees.OrderFeesCreateReqVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.rentOrder.RentOrderCountVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.bill.vo.rentOrder.RentOrderVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.*;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.company.*;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.customerCompany.CustomerCompanyRespVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.ownerContract.ChangeHousekeepReqVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.ownerContract.ContractHouseOwnerRespVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.ownerContract.ContractOwnerRoomRefVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.settlement.SettlementDetailBaseVO;
import com.xiaoshuidi.cloud.module.contract.controller.admin.home.vo.HomeData;
import com.xiaoshuidi.cloud.module.contract.controller.admin.roommate.vo.ContractRoommateListReqVO;
import com.xiaoshuidi.cloud.module.contract.controller.app.contract.vo.RenterCheckRespVO;
import com.xiaoshuidi.cloud.module.contract.convert.contract.*;
import com.xiaoshuidi.cloud.module.contract.convert.roommeta.ContractRoommateConvert;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.approval.ContractActiveAssociationDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.bill.OrderFeesDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.bill.RentOrderDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.contract.*;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.roommeta.ContractRoommateDO;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.approval.ContractActiveAssociationMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.bill.OrderFeesMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.bill.RentOrderMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.CompanyContractMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.ContractRoomMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.SettlementDetailMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.SettlementMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.roommeta.ContractRoommateMapper;
import com.xiaoshuidi.cloud.module.contract.enums.ApiConstants;
import com.xiaoshuidi.cloud.module.contract.enums.ApproveTypeEnum;
import com.xiaoshuidi.cloud.module.contract.enums.ErrorCodeConstants;
import com.xiaoshuidi.cloud.module.contract.enums.bill.*;
import com.xiaoshuidi.cloud.module.contract.enums.contract.*;
import com.xiaoshuidi.cloud.module.contract.enums.settlement.SettlementStatusEnum;
import com.xiaoshuidi.cloud.module.contract.service.approval.ContractApprovalService;
import com.xiaoshuidi.cloud.module.contract.service.bill.OrderFeesService;
import com.xiaoshuidi.cloud.module.contract.service.bill.RentOrderService;
import com.xiaoshuidi.cloud.module.contract.service.contract.*;
import com.xiaoshuidi.cloud.module.contract.service.contract.dto.*;
import com.xiaoshuidi.cloud.module.contract.service.home.HomeService;
import com.xiaoshuidi.cloud.module.contract.service.roommate.ContractRoommateService;
import com.xiaoshuidi.cloud.module.contract.util.bill.BillPeriod;
import com.xiaoshuidi.cloud.module.contract.util.date.DateUtils;
import com.xiaoshuidi.cloud.module.contract.vo.ContractRoomReqVO;
import com.xiaoshuidi.cloud.module.contract.vo.ContractRoomRespVO;
import com.xiaoshuidi.cloud.module.finance.api.invoice.InvoiceApi;
import com.xiaoshuidi.cloud.module.iot.common.ResultModel;
import com.xiaoshuidi.cloud.module.member.api.message.MemberMessageApi;
import com.xiaoshuidi.cloud.module.member.api.message.dto.MemberMessageDTO;
import com.xiaoshuidi.cloud.module.member.api.user.MemberUserApi;
import com.xiaoshuidi.cloud.module.member.api.user.dto.EmergencyContact;
import com.xiaoshuidi.cloud.module.member.api.user.dto.MemberUserReqDTO;
import com.xiaoshuidi.cloud.module.member.api.user.dto.MemberUserRespDTO;
import com.xiaoshuidi.cloud.module.rooms.api.home.HomeApi;
import com.xiaoshuidi.cloud.module.rooms.api.home.vo.RoomLayoutVO;
import com.xiaoshuidi.cloud.module.rooms.api.reserve.ReserveApi;
import com.xiaoshuidi.cloud.module.rooms.api.reserve.vo.ReserveGetApiResp;
import com.xiaoshuidi.cloud.module.rooms.api.roomhouse.RoomHouseApi;
import com.xiaoshuidi.cloud.module.rooms.api.roomhouse.vo.UpdateContractInfoByRenewalReqVO;
import com.xiaoshuidi.cloud.module.rooms.api.roomstate.RoomStateRecordApi;
import com.xiaoshuidi.cloud.module.rooms.api.roomstate.dto.LivingWithInfoDTO;
import com.xiaoshuidi.cloud.module.rooms.api.roomstate.dto.RoomContractInfoDTO;
import com.xiaoshuidi.cloud.module.rooms.api.roomstate.dto.RoomTenantsInfoDTO;
import com.xiaoshuidi.cloud.module.rooms.api.roomstate.dto.UpdateRoomStateReqDTO;
import com.xiaoshuidi.cloud.module.rooms.api.roomstate.vo.GetCurrentRecordRespVO;
import com.xiaoshuidi.cloud.module.rooms.api.sdiot.IotRoomDeviceApi;
import com.xiaoshuidi.cloud.module.rooms.enums.RentStatusEnum;
import com.xiaoshuidi.cloud.module.rooms.enums.RoomContractStatusEnum;
import com.xiaoshuidi.cloud.module.rooms.enums.RoomHouseReserveStatus;
import com.xiaoshuidi.cloud.module.rooms.vo.RoomHouseRespVo;
import com.xiaoshuidi.cloud.module.system.api.sms.SmsSendApi;
import com.xiaoshuidi.cloud.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO;
import com.xiaoshuidi.cloud.module.system.api.user.AdminUserApi;
import com.xiaoshuidi.cloud.module.system.api.user.dto.AdminUserRespDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.formula.functions.T;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.xiaoshuidi.cloud.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.xiaoshuidi.cloud.framework.security.core.util.SecurityFrameworkUtils.getLoginUser;
import static com.xiaoshuidi.cloud.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static com.xiaoshuidi.cloud.module.contract.enums.ApiConstants.DEPOSIT_STR;
import static com.xiaoshuidi.cloud.module.contract.enums.ApiConstants.PAY_STR;
import static com.xiaoshuidi.cloud.module.contract.enums.ErrorCodeConstants.*;

/**
 * 租客合同 Service 实现类
 *
 * @author 合房租赁
 */
@Slf4j
@Service
public class ContractRoomServiceImpl implements ContractRoomService {
    @Resource
    private ContractRoomMapper contractRoomMapper;
    @Resource
    private RentOrderService rentOrderService;
    @Resource
    private OrderFeesService orderFeesService;
    @Resource
    private ContractExtrasDataService contractExtrasDataService;
    @Resource
    private TimebaseSequenceGenerator timebaseSequenceGenerator;
    @Resource
    private CustomerCompanyService customerCompanyService;
    @Resource
    private RoomHouseApi roomHouseApi;
    @Resource
    private IotRoomDeviceApi iotRoomDeviceApi;
    @Resource
    private RoomStateRecordApi roomStateRecordApi;
    @Resource
    private BpmProcessInstanceApi bpmProcessInstanceApi;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private MemberUserApi memberUserApi;
    @Resource
    private ContractApprovalService contractApprovalService;
    @Resource
    private ContractRoommateMapper contractRoommateMapper;
    @Resource
    private ContractRoommateService contractRoommateService;
    @Resource
    private SettlementMapper settlementMapper;
    @Resource
    private SettlementDetailMapper settlementDetailMapper;
    @Resource
    private ContractActiveAssociationMapper contractActiveAssociationMapper;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Value("${spring.application.name}")
    private String applicationName;
    @Resource
    private RentOrderMapper rentOrderMapper;
    @Resource
    private OrderFeesMapper orderFeesMapper;
    @Resource
    private ContractChangingRoomsRecordServiceImpl contractChangingRoomsRecordService;
    @Resource
    private ContractStayService contractStayService;
    @Resource
    private MemberMessageApi memberMessageApi;
    @Resource
    private LeaseSystemEventProducer leaseSystemEventProducer;
    @Resource
    private CompanyContractService companyContractService;
    @Resource
    private ReserveApi reserveApi;
    @Resource
    private HomeService homeService;
    @Resource
    private CompanyContractMapper companyContractMapper;
    @Resource
    private ContractHouseOwnerService contractHouseOwnerService;
    @Resource
    private SmsSendApi smsSendApi;
    @Resource
    private InvoiceApi invoiceApi;
    @Resource
    private HomeApi homeApi;

    //新建合同
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createContractBill(ContractCreateReqVO createReqVO) {
        // 校验存在
        validateCreate(createReqVO);
        // 校验分散式房源业主合同
        if (!checkDisperseRoomEnterContract(Arrays.asList(createReqVO.getRoomId()),createReqVO.getStartTime(),createReqVO.getEndTime())){
            throw exception(DISPERSE_ROOM_OWNER_CONTRACT_FAILURE);
        }
        CommonResult<RoomHouseRespVo> roomHouseinfoById = roomHouseApi.getRoomHouseinfoById(createReqVO.getRoomId());
        if (roomHouseinfoById.isSuccess()) {
            createReqVO.setRoomName(roomHouseinfoById.getData().getFullRoomName());
        }
        // 根据账单计算账单金额  @Schema(description = "入住交房时间")
        RentOrderVO rentOrderVO = ContractConvert.INSTANCE.convertToRentOrder(createReqVO);
        if (createReqVO.getBooking() != null && createReqVO.getBooking().getBookingDeposit() != null) {
            rentOrderVO.setPaidRentUtilities(createReqVO.getBooking().getBookingDeposit());
        }
        OrderResp orderResp = new OrderResp();
        if (Objects.nonNull(rentOrderVO.getBillRules()) && rentOrderVO.getBillRules() == 1){
            // 自然月生成账单
            orderResp = DateRangeGenerator.calculateNaturalMonthBillAmount(rentOrderVO);
        }else {
            // 按非自然月生成账单
            // 根据账单计算账单金额
            orderResp = DateRangeGenerator.calculateBillAmount(rentOrderVO);
        }
        createReqVO.setTotalRent(orderResp.getTotalRent());
        //创建合同
        ContractRoomDO contractRoomDO = this.create(createReqVO, ContractNodeEnum.NEW.getCode());
        //组装生成账单的数据
        rentOrderVO.setContractId(contractRoomDO.getId());
        rentOrderVO.setOrderFeesVo(createReqVO.getOrderFeesVo());
        if (CustomerTypeEnum.PERSONAL.getCode().equals(contractRoomDO.getCustomerType())) {
            log.info("个人客户设置账单member_id");
            rentOrderVO.setMemberId(contractRoomDO.getMemberId());
            rentOrderVO.setOperatorName(SecurityFrameworkUtils.getLoginUser().getNickname());
        }
        //生成 账单集合
        createBill(rentOrderVO, orderResp);
    }

    //续租
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createRenewalContractBill(ContractRenewalReqVO reqVO) {
        // 校验存在
        validateRenewalCreate(reqVO);
        // 分散式房源校验业主合同是否有效
        if (!checkDisperseRoomEnterContract(Arrays.asList(reqVO.getRoomId()),reqVO.getStartTime(),reqVO.getEndTime())){
            throw exception(DISPERSE_ROOM_OWNER_CONTRACT_FAILURE);
        }
        CommonResult<RoomHouseRespVo> roomHouseinfoById = roomHouseApi.getRoomHouseinfoById(reqVO.getRoomId());
        if (roomHouseinfoById.isSuccess()) {
            reqVO.setRoomName(roomHouseinfoById.getData().getFullRoomName());
        }
        ContractCreateReqVO createReqVO = ContractConvert.INSTANCE.convert(reqVO);
        Long fromId = reqVO.getFromId();
        //查询原合同的的押金
        ContractRoomDO roomContract = contractRoomMapper.getRoomContractById(fromId);
        reqVO.setOldDeposit(roomContract.getDeposit());
        // 根据账单计算账单金额
        RentOrderVO rentOrderVO = ContractConvert.INSTANCE.convertToRentOrder(reqVO);
        rentOrderVO.setPaidRentUtilities(reqVO.getOldDeposit());
        OrderResp orderResp = new OrderResp();
        if (Objects.nonNull(rentOrderVO.getBillRules()) && rentOrderVO.getBillRules() == 1){
            // 自然月生成账单
            orderResp = DateRangeGenerator.calculateNaturalMonthBillAmount(rentOrderVO);
        }else {
            // 按非自然月生成账单
            // 根据账单计算账单金额
            orderResp = DateRangeGenerator.calculateBillAmount(rentOrderVO);
        }
        createReqVO.setTotalRent(orderResp.getTotalRent());
        // 原合同押金大于续租合同总金额, 多出来的押金记录到续租合同的押金上
//        if (reqVO.getOldDeposit().compareTo(orderResp.getTotalAmount()) > 0) {
//            OrderDetailVo first = orderResp.getOrderDetailVos().get(0);
//            BigDecimal d = reqVO.getOldDeposit().subtract(orderResp.getTotalAmount());
//            first.setDepositAmount(first.getDepositAmount().add(d));
//            first.setTotalAmount(first.getTotalAmount().add(d));
//            OrderFeesCreateReqVO deposit = first.getFeeDetail().stream().filter(f -> f.getFeeSort().equals(OrderFeeSortTypeEnum.DEPOSIT.getCode()))
//                    .findFirst().orElse(null);
//            if (deposit != null) {
//                deposit.setAmount(deposit.getAmount().add(d));
//                deposit.setPaidAmount(deposit.getPaidAmount().add(d));
//            }
//        }
        ContractRoomDO contractRoomDO = this.create(createReqVO, ContractNodeEnum.RENEWAL.getCode());
        if (contractRoomDO == null){
            return;
        }
        //组装生成账单的数据
        rentOrderVO.setContractId(contractRoomDO.getId());
        rentOrderVO.setOrderFeesVo(createReqVO.getOrderFeesVo());
        if (CustomerTypeEnum.PERSONAL.getCode().equals(contractRoomDO.getCustomerType())) {
            log.info("个人客户设置账单member_id");
            rentOrderVO.setMemberId(contractRoomDO.getMemberId());
            rentOrderVO.setOperatorName(SecurityFrameworkUtils.getLoginUser().getNickname());
        }
        //生成 账单集合
        createBill(rentOrderVO, orderResp);
        GetCurrentRecordRespVO currentRecordRespVO = roomStateRecordApi.getCurrentRecord(reqVO.getRoomId()).getData();
        String state = currentRecordRespVO.getState();
        if (state.equals(RentStatusEnum.OVERDUE.getValue())) {
            UpdateRoomStateReqDTO updateRoomStateReqDTO = new UpdateRoomStateReqDTO();
            updateRoomStateReqDTO.setRoomHouseId(reqVO.getRoomId());
            updateRoomStateReqDTO.setRoomContractStatusEnum(RoomContractStatusEnum.CHECK_IN);
            roomStateRecordApi.updateRoomStateByContract(updateRoomStateReqDTO);
        }
    }

    private void validateRenewalCreate(ContractRenewalReqVO reqVO) {
        // 待审批的合同 退租合同  是否存在续租合同
        Integer renewalContractRoomDO = contractRoomMapper.getRenewalContract(reqVO.getFromId());
        if (renewalContractRoomDO > 0) {
            throw exception(CONTRACT_NO_RENEWAL);
        }
        if (reqVO.getFromId() == 0) {
            throw exception(RENEWAL_CONTRACT_FROM_ID);
        }
        //只有已入驻的合同才能续租
        Long count = contractRoomMapper.selectCount(new LambdaQueryWrapper<ContractRoomDO>().eq(ContractRoomDO::getId, reqVO.getFromId())
                .ne(ContractRoomDO::getContractStatus, ContractStatusEnum.CHECK_IN.getCode()));
        if (count > 0){
            throw exception(ONLY_RENEW_THE_CHECKED_IN_CONTRACT);
        }
        //根据原合同id查询原合同 什么时候到期
        ContractRoomDO contractRoomDO = get(reqVO.getFromId());
        LocalDate endTime = contractRoomDO.getEndTime();
//        long day = BillPeriod.totalDays(endTime, LocalDate.now());
//        if (day > 15 || day < 0) {
//            throw exception(CONTRACT_PASS_REQUIRE_DAY);
//        }
        // 是否存在未支付的账单
        rentOrderService.unpaidOrder(endTime, Collections.singletonList(reqVO.getFromId()), Arrays.asList(OrderTypeEnum.DEFAULT.getCode(), OrderTypeEnum.CUSTOM.getCode()));
    }

    @Override
    public void createExchangeContractBill(ContractExchangeReqVO reqVO) {
        // 是否有  正在审核中的退租、换租、续租
        validateExchangeCreate(reqVO);
        if (!checkDisperseRoomEnterContract(Arrays.asList(reqVO.getRoomId()),reqVO.getStartTime(),reqVO.getEndTime())){
            throw exception(DISPERSE_ROOM_OWNER_CONTRACT_FAILURE);
        }
        CommonResult<RoomHouseRespVo> roomHouseinfoById = roomHouseApi.getRoomHouseinfoById(reqVO.getRoomId());
        if (roomHouseinfoById.isSuccess()) {
            reqVO.setRoomName(roomHouseinfoById.getData().getFullRoomName());
        }
        //企业合同自合同换房不更改原合同状态，先获取原企业合同状态。下面的退租操作可能会更改原合同状态
        String oldStatus = null;
        ContractRoomDO oldContract = contractRoomMapper.selectById(reqVO.getContractRefundReqVO().getContractId());
        if (oldContract != null && oldContract.getCompanyContractId() != null){
            CompanyContractDO companyContractDO = companyContractMapper.selectCompanyContractById(oldContract.getCompanyContractId());
            oldStatus = companyContractDO.getStatus();
        }
        //换房生成 结算单
        ContractRefundReqVO refundReqVO = reqVO.getContractRefundReqVO();
        //应收  直接生成结算单
        createExchangeRefund(refundReqVO);
        ContractCreateReqVO createReqVO = ContractConvert.INSTANCE.convert(reqVO);

        //如果oldStatus不等于null，进行重新赋值
        if (StringUtils.isNotBlank(oldStatus)){
            CompanyContractDO companyContractDO = new CompanyContractDO();
            companyContractDO.setId(oldContract.getCompanyContractId());
            companyContractDO.setStatus(oldStatus);
            companyContractMapper.updateById(companyContractDO);
        }
        // 根据账单计算账单金额
        RentOrderVO rentOrderVO = ContractConvert.INSTANCE.convertToRentOrder(reqVO);
        //应收应退 金额
        BigDecimal amount = refundReqVO.getAmount();

        if ("out".equals(refundReqVO.getFlowType())) {
            //应退
            if (amount.compareTo(BigDecimal.ZERO) > 0) {
                rentOrderVO.setPaidRentUtilities(refundReqVO.getFirstBillPid());
            } else {
                rentOrderVO.setPaidRentUtilities(amount);
            }
        }
        // 抵扣新合同账单
        if ("in".equals(refundReqVO.getFlowType())) {
            // 原房间应收金额
            // oldRoomSettlementVos 新参数字段
            BigDecimal oldRoomPayable = CollectionUtils.isEmpty(refundReqVO.getOldRoomSettlementVos()) ?
                    BigDecimal.ZERO :
                    refundReqVO.getOldRoomSettlementVos().stream().filter(s -> "in".equals(s.getFlowType()))
                            .map(SettlementDetailBaseVO::getRefundableAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            rentOrderVO.setPaidRentUtilities(refundReqVO.getRefundableAmount().subtract(oldRoomPayable));
        }
        OrderResp orderResp = new OrderResp();
        if (Objects.nonNull(rentOrderVO.getBillRules()) && rentOrderVO.getBillRules() == 1){
            // 自然月生成账单
            orderResp = DateRangeGenerator.calculateNaturalMonthBillAmount(rentOrderVO);
        }else {
            // 按非自然月生成账单
            // 根据账单计算账单金额
            orderResp = DateRangeGenerator.calculateBillAmount(rentOrderVO);
        }
        createReqVO.setActualEndTime(refundReqVO.getEvictionTime());
        createReqVO.setTotalRent(orderResp.getTotalRent());
        createReqVO.setActualEndTime(null);

        ContractRoomDO contractRoomDO = this.create(createReqVO, ContractNodeEnum.EXCHANGE.getCode());
        //取消原合同未付账单
        Long fromId = createReqVO.getFromId();
        List<RentOrderDO> rentorder = rentOrderService.getRentOrderByBusinessId(fromId);
        if (CollectionUtils.isNotEmpty(rentorder)) {
            List<Long> collect = rentorder.stream().map(s -> s.getId()).collect(Collectors.toList());
            //换房 取消未支付的账单 部分支付的改为已支付
            cancelRentorder(rentorder);
            if (CollectionUtils.isNotEmpty(collect)) {
                List<OrderFeesDO> orderFeesDOS = orderFeesService.getOrderFeeByorderIds(collect, Arrays.asList(OrderTypeEnum.DEFAULT.getCode(), OrderTypeEnum.CUSTOM.getCode()));
                cancelorderFeesDOS(orderFeesDOS);
            }
        }
        //组装生成账单的数据
        rentOrderVO.setContractId(contractRoomDO.getId());
        rentOrderVO.setOrderFeesVo(reqVO.getOrderFeesVo());
        if (CustomerTypeEnum.PERSONAL.getCode().equals(contractRoomDO.getCustomerType())) {
            log.info("个人客户设置账单member_id");
            rentOrderVO.setMemberId(contractRoomDO.getMemberId());
            rentOrderVO.setOperatorName(SecurityFrameworkUtils.getLoginUser().getNickname());
        }
        //生成 账单集合
        createBill(rentOrderVO, orderResp);

        log.info("换房合同信息：{}", JSONObject.toJSONString(contractRoomDO));
        log.info("换房账单信息：{}", JSONObject.toJSONString(orderResp));
        if (ContractStatusEnum.ACTIVE.getCode().equals(contractRoomDO.getContractStatus())
                && CollectionUtils.isNotEmpty(orderResp.getOrderDetailVos())){
            // 合同已生效且首期账单完全抵扣 原合同直接释放
            List<OrderDetailVo> orderDetailVos = orderResp.getOrderDetailVos();
            OrderDetailVo orderDetailVo = orderDetailVos.stream().filter(d -> d.getStartTime().equals(contractRoomDO.getStartTime())).findFirst().orElse(null);
            if (Objects.nonNull(orderDetailVo) && OrderPayStatusEnum.PAID.getCode().equals(orderDetailVo.getPayStatus())){
                this.dealOldContract(contractRoomDO.getId(),ContractNodeEnum.EXCHANGE.getCode());
            }
        }
    }


    private void createExchangeRefund(ContractRefundReqVO reqVO) {
        LocalDate evictionTime = reqVO.getEvictionTime();
        Long contractId = reqVO.getContractId();
        //查询退租前的账单是否有未支付的账单 结算单
        rentOrderService.unpaidOrder(evictionTime, Collections.singletonList(contractId), Arrays.asList(OrderTypeEnum.DEFAULT.getCode(), OrderTypeEnum.CUSTOM.getCode()));
        ContractRoomDO renewContract = getByFromId(reqVO.getContractId());
        if (renewContract != null && LocalDate.now().isBefore(renewContract.getStartTime())) {
            throw new ServiceException(REFUND.getCode(), "存在续租合同, 请前往续租合同进行操作");
        }
        ContractSettlementDTO settlementDTO = handleExchangeSettlementDetail(reqVO);
        generateSettlement(settlementDTO.getSettlementDO(), settlementDTO.getList());
    }
    private ContractSettlementDTO handleExchangeSettlementDetail(ContractRefundReqVO reqVO) {
        // 原合同信息
        ContractRoomDO contractRoomDO = contractRoomMapper.selectById(reqVO.getContractId());
        if (!isBoolean(contractRoomDO.getContractStatus())) {
            throw exception(REFUND);
        }
        if (contractRoomDO.getStartTime().isAfter(LocalDate.now())) {
            throw new ServiceException(REFUND.getCode(), "合同未生效, 无法操作退租");
        }

        ContractSettlementDTO dto = new ContractSettlementDTO();
        List<SettlementDetailBaseVO> settlementVos = reqVO.getSettlementVos()
                .stream()
                //不进行明细过滤
//                .filter(item -> item.getRefundableAmount().compareTo(BigDecimal.ZERO) != 0)
                .peek(s -> {
                    if (s.getAmount() == null) {
                        s.setAmount(s.getRefundableAmount());
                    }
                    if (s.getOrderStartDate() == null) {
                        s.setOrderStartDate(contractRoomDO.getStartTime().atStartOfDay());
                    }
                    if (s.getOrderEndDate() == null) {
                        s.setOrderEndDate(reqVO.getEvictionTime().atStartOfDay());
                    }
                })
                .collect(Collectors.toList());
        Map<String, List<SettlementDetailBaseVO>> map = settlementVos.stream()
                .collect(Collectors.groupingBy(SettlementDetailBaseVO::getFlowType));
        List<SettlementDetailBaseVO> outAmount = map.get("out");
        List<SettlementDetailBaseVO> inAmount = map.get("in");
        BigDecimal receivablesAmount = BigDecimal.ZERO;
        if (CollectionUtils.isNotEmpty(inAmount)) {
            for (SettlementDetailBaseVO settlementDetailBaseVO : inAmount) {
                receivablesAmount = receivablesAmount.add(settlementDetailBaseVO.getRefundableAmount());
            }
        }
        BigDecimal discountValue = reqVO.getDiscountValue() == null ? BigDecimal.ZERO : reqVO.getDiscountValue();
        if (receivablesAmount.subtract(discountValue).compareTo(reqVO.getReceivablesAmount()) != 0) {
            throw exception(RECEIVABLES_AMOUNT_INCONSISTENT);
        }
        BigDecimal refundableAmount = BigDecimal.ZERO;
        if (CollectionUtils.isNotEmpty(outAmount)) {
            for (SettlementDetailBaseVO settlementDetailBaseVO : outAmount) {
                refundableAmount = refundableAmount.add(settlementDetailBaseVO.getRefundableAmount());
            }
        }
        if (refundableAmount.compareTo(reqVO.getRefundableAmount()) != 0) {
            throw exception(REFUNDABLE_AMOUNT_INCONSISTENT);
        }
        BigDecimal amount = reqVO.getAmount();
        if ("out".equals(reqVO.getFlowType())) {
            amount = amount.negate();
        }
        if (receivablesAmount.subtract(refundableAmount).subtract(discountValue).compareTo(amount) != 0) {
            throw exception(AMOUNT_INCONSISTENT);
        }

        SettlementDO settlementDO = SettlementConvert.INSTANCE.convert(reqVO);
        settlementDO.setApartmentId(contractRoomDO.getApartmentId());
        settlementDO.setContractNo(contractRoomDO.getContractNo());
        settlementDO.setApartmentName(contractRoomDO.getApartmentName());
        settlementDO.setRoomId(contractRoomDO.getRoomId());
        settlementDO.setRoomName(contractRoomDO.getRoomName());
        settlementDO.setCustomerName(contractRoomDO.getCustomerName());
        settlementDO.setCustomerPhone(contractRoomDO.getCustomerPhone());
        settlementDO.setSettlementType(CustomerTypeEnum.PERSONAL.getCode().equals(contractRoomDO.getCustomerType()) ?
                ContractSettlementTypeEnum.DEFAULT.getValue() : ContractSettlementTypeEnum.COMPANY.getValue());
        CommonResult<RoomHouseRespVo> roomHouse = roomHouseApi.getRoomHouseinfoById(contractRoomDO.getRoomId());
        if (roomHouse.isSuccess()) {
            settlementDO.setApartmentAddr(roomHouse.getData().getAddress());
            settlementDO.setRoomFullName(roomHouse.getData().getFullRoomName());
        }

        String roomEquipmentsJson = contractStayService.getRoomEquipmentsJson(contractRoomDO.getRoomId());
        settlementDO.setRoomAsset(roomEquipmentsJson);
//        //根据公寓id 查询是否需要退租审批
//        //查看是是企业子合同审批还是个人子合同审批
//        String mark;
//        if (contractRoomDO.getCustomerType().equals(CustomerTypeEnum.PERSONAL.getCode())){
//            mark = contractApprovalService.getByRoomIdAndMark(contractRoomDO.getApartmentId(), ApproveTypeEnum.RENTING_OUT_CONTRACT.getMsg());
//        }else {
//            mark = contractApprovalService.getByRoomIdAndMark(contractRoomDO.getApartmentId(), ApproveTypeEnum.RENTING_OUT_COMPANY_CONTRACT.getMsg());
//        }

//        String settlementStatus = SettlementStatusEnum.NO_AUDIT_REQUITED.getCode();
//        //退租审批
//        if (needApproval && StringUtils.isNotBlank(mark)) {
//            settlementStatus = SettlementStatusEnum.IN_REVIEW.getCode();
//            ContractStatusDTO contractStatusDTO = new ContractStatusDTO();
//            contractStatusDTO.setNeedApproval(true);
//            dto.setContractStatus(ContractStatusEnum.PENDING.getCode());
//
//            approve(reqVO.getContractId(), ContractNodeEnum.RENTING_OUT.getCode(), contractStatusDTO, contractRoomDO);
//            settlementDO.setProcessInstanceId(contractStatusDTO.getProcessInstanceId());
//        }
        ContractRoomDO updateDO = new ContractRoomDO();
        //修改 合同状态为 非正常结束
        updateDO.setId(contractRoomDO.getId());
//        updateDO.setContractNode(ContractNodeEnum.RENTING_OUT.getCode());
        updateDO.setOldContractNode(contractRoomDO.getContractNode());
        updateDO.setOldContractStatus(contractRoomDO.getContractStatus());
        updateDO.setActualEndTime(reqVO.getEvictionTime());
        //换房不需要审批
//        if (StringUtils.isBlank(mark)) {
        String settlementStatus = SettlementStatusEnum.NO_AUDIT_REQUITED.getCode();
        String contractStatus;
        //正常退租
        if (EvictionTypeEnum.NORMAL_RENTING.getCode().equals(reqVO.getEvictionType())) {
            contractStatus = ContractStatusEnum.NORMAL_END.getCode();
        } else {
            contractStatus = ContractStatusEnum.ABNORMAL_END.getCode();
        }
        dto.setContractStatus(contractStatus);

        // 合同状态改为冻结
        updateDO.setContractStatus(ContractStatusEnum.FREEZE.getCode());

        contractRoomMapper.updateById(updateDO);
        //修改合同未支付账单为冻结
        LambdaUpdateWrapper<RentOrderDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(RentOrderDO::getBusinessId, contractRoomDO.getId());
        updateWrapper.eq(RentOrderDO::getOrderType, OrderTypeEnum.DEFAULT.getCode());
        updateWrapper.eq(RentOrderDO::getPayStatus, OrderPayStatusEnum.UNPAID.getCode());
        updateWrapper.set(RentOrderDO::getPayStatus, OrderPayStatusEnum.FREEZE.getCode());
        rentOrderMapper.update(null,updateWrapper);

        // 生成退租清算账单
        createCheckoutSettlementRentOrder(contractRoomDO, settlementDO, settlementVos);

//        } else {
//            updateDO.setContractStatus(ContractStatusEnum.PENDING.getCode());
//            updateDO.setOldContractStatus(contractRoomDO.getContractStatus());
//            updateDO.setOldContractNode(contractRoomDO.getContractNode());
//        }
        updateDO.setEvictionType(reqVO.getEvictionType());
        updateDO.setEvictionTime(reqVO.getEvictionTime());
        updateDO.setEvictionReason(reqVO.getEvictionReason());
        contractRoomMapper.updateById(updateDO);
        settlementDO.setClearedStatus(settlementStatus);
        dto.setContract(contractRoomDO);
        dto.setSettlementDO(settlementDO);
        dto.setList(settlementVos);
        log.info("{合同状态为：{}}", dto.getContract().getContractNode());
        return dto;
    }

    private void cancelRentorder(List<RentOrderDO> rentorder) {
        //未支付
        List<RentOrderDO> unpaidRentorder = rentorder.stream().filter(p -> OrderPayStatusEnum.UNPAID.getCode().equals(p.getPayStatus())).collect(Collectors.toList());
        for (RentOrderDO rentOrderDO : unpaidRentorder) {
            rentOrderDO.setPayStatus(OrderPayStatusEnum.CANCEL.getCode());
            rentOrderMapper.updateById(rentOrderDO);
        }
        //部分支付
        List<RentOrderDO> partRentorder = rentorder.stream().filter(p -> OrderPayStatusEnum.PART.getCode().equals(p.getPayStatus())).collect(Collectors.toList());
        for (RentOrderDO rentOrderDO : partRentorder) {
            rentOrderDO.setPayStatus(OrderPayStatusEnum.PAID.getCode());
            rentOrderMapper.updateById(rentOrderDO);
        }

    }

    private void cancelorderFeesDOS(List<OrderFeesDO> orderFeesDOS) {
        List<OrderFeesDO> unpaidOrderFeesDOS = orderFeesDOS.stream().filter(p -> OrderPayStatusEnum.UNPAID.getCode().equals(p.getPayStatus())).collect(Collectors.toList());
        for (OrderFeesDO orderFeesDO : unpaidOrderFeesDOS) {
            orderFeesDO.setPayStatus(OrderPayStatusEnum.CANCEL.getCode());
            orderFeesMapper.updateById(orderFeesDO);
        }
        List<OrderFeesDO> partOrderFeesDOS = orderFeesDOS.stream().filter(p -> OrderPayStatusEnum.PART.getCode().equals(p.getPayStatus())).collect(Collectors.toList());
        for (OrderFeesDO orderFeesDO : partOrderFeesDOS) {
            orderFeesDO.setPayStatus(OrderPayStatusEnum.PAID.getCode());
            orderFeesMapper.updateById(orderFeesDO);
        }
    }

    /*
     * 租客合同-退租
     * */
    @Override
    @Transactional
    public void createRefund(ContractRefundReqVO reqVO, Boolean flag) {
        LocalDate evictionTime = reqVO.getEvictionTime();
        Long contractId = reqVO.getContractId();
        //查询退租前的账单是否有未支付的账单
        //未入住不存在账单问题
        rentOrderService.unpaidOrder(evictionTime, Collections.singletonList(contractId), Arrays.asList(OrderTypeEnum.DEFAULT.getCode(), OrderTypeEnum.CUSTOM.getCode()));
        ContractRoomDO renewContract = getByFromId(reqVO.getContractId());
        if (renewContract != null && LocalDate.now().isBefore(renewContract.getStartTime())) {
            throw new ServiceException(REFUND.getCode(), "存在续租合同, 请前往续租合同进行操作");
        }
        ContractSettlementDTO settlementDTO = handleSettlemendDetail(reqVO, flag);
        generateSettlement(settlementDTO.getSettlementDO(), settlementDTO.getList());

    }

    public ContractSettlementDTO handleSettlemendDetail(ContractRefundReqVO reqVO, Boolean needApproval) {
        ContractRoomDO contractRoomDO = contractRoomMapper.selectById(reqVO.getContractId());
        if (!isBoolean(contractRoomDO.getContractStatus())) {
            throw exception(REFUND);
        }
        /*if (contractRoomDO.getStartTime().isAfter(LocalDate.now())) {
            throw new ServiceException(REFUND.getCode(), "合同未生效, 无法操作退租");
        }*/

        ContractSettlementDTO dto = new ContractSettlementDTO();
        List<SettlementDetailBaseVO> settlementVos = reqVO.getSettlementVos()
                .stream()
                //不进行明细过滤
//                .filter(item -> item.getRefundableAmount().compareTo(BigDecimal.ZERO) != 0)
                .peek(s -> {
                    if (s.getAmount() == null) {
                        s.setAmount(s.getRefundableAmount());
                    }
                    if (s.getOrderStartDate() == null) {
                        s.setOrderStartDate(contractRoomDO.getStartTime().atStartOfDay());
                    }
                    if (s.getOrderEndDate() == null) {
                        s.setOrderEndDate(reqVO.getEvictionTime().atStartOfDay());
                    }
                })
                .collect(Collectors.toList());
        Map<String, List<SettlementDetailBaseVO>> map = settlementVos.stream()
                .collect(Collectors.groupingBy(SettlementDetailBaseVO::getFlowType));
        List<SettlementDetailBaseVO> outAmount = map.get("out");
        List<SettlementDetailBaseVO> inAmount = map.get("in");
        BigDecimal receivablesAmount = BigDecimal.ZERO;
        if (CollectionUtils.isNotEmpty(inAmount)) {
            for (SettlementDetailBaseVO settlementDetailBaseVO : inAmount) {
                receivablesAmount = receivablesAmount.add(settlementDetailBaseVO.getRefundableAmount());
            }
        }
        BigDecimal discountValue = reqVO.getDiscountValue() == null ? BigDecimal.ZERO : reqVO.getDiscountValue();
        if (receivablesAmount.subtract(discountValue).compareTo(reqVO.getReceivablesAmount()) != 0) {
            throw exception(RECEIVABLES_AMOUNT_INCONSISTENT);
        }
        BigDecimal refundableAmount = BigDecimal.ZERO;
        if (CollectionUtils.isNotEmpty(outAmount)) {
            for (SettlementDetailBaseVO settlementDetailBaseVO : outAmount) {
                refundableAmount = refundableAmount.add(settlementDetailBaseVO.getRefundableAmount());
            }
        }
        if (refundableAmount.compareTo(reqVO.getRefundableAmount()) != 0) {
            throw exception(REFUNDABLE_AMOUNT_INCONSISTENT);
        }
        BigDecimal amount = reqVO.getAmount();
        if ("out".equals(reqVO.getFlowType())) {
            amount = amount.negate();
        }
        if (receivablesAmount.subtract(refundableAmount).subtract(discountValue).compareTo(amount) != 0) {
            throw exception(AMOUNT_INCONSISTENT);
        }

        SettlementDO settlementDO = SettlementConvert.INSTANCE.convert(reqVO);
        settlementDO.setApartmentId(contractRoomDO.getApartmentId());
        settlementDO.setContractNo(contractRoomDO.getContractNo());
        settlementDO.setApartmentName(contractRoomDO.getApartmentName());
        settlementDO.setRoomId(contractRoomDO.getRoomId());
        settlementDO.setRoomName(contractRoomDO.getRoomName());
        settlementDO.setCustomerName(contractRoomDO.getCustomerName());
        settlementDO.setCustomerPhone(contractRoomDO.getCustomerPhone());
        settlementDO.setSettlementType(CustomerTypeEnum.PERSONAL.getCode().equals(contractRoomDO.getCustomerType()) ?
                ContractSettlementTypeEnum.DEFAULT.getValue() : ContractSettlementTypeEnum.COMPANY.getValue());
        CommonResult<RoomHouseRespVo> roomHouse = roomHouseApi.getRoomHouseinfoById(contractRoomDO.getRoomId());
        if (roomHouse.isSuccess()) {
            settlementDO.setApartmentAddr(roomHouse.getData().getAddress());
            settlementDO.setRoomFullName(roomHouse.getData().getFullRoomName());
        }

        String roomEquipmentsJson = contractStayService.getRoomEquipmentsJson(contractRoomDO.getRoomId());
        settlementDO.setRoomAsset(roomEquipmentsJson);
        //根据公寓id 查询是否需要退租审批
        //查看是是企业子合同审批还是个人子合同审批
        String mark;
        if (contractRoomDO.getCustomerType().equals(CustomerTypeEnum.PERSONAL.getCode())){
            mark = contractApprovalService.getByRoomIdAndMark(contractRoomDO.getApartmentId(), ApproveTypeEnum.RENTING_OUT_CONTRACT.getMsg());
        }else {
            mark = contractApprovalService.getByRoomIdAndMark(contractRoomDO.getApartmentId(), ApproveTypeEnum.RENTING_OUT_COMPANY_CONTRACT.getMsg());
        }

        String settlementStatus = SettlementStatusEnum.NO_AUDIT_REQUITED.getCode();
        //退租审批
        if (needApproval && StringUtils.isNotBlank(mark)) {
            settlementStatus = SettlementStatusEnum.IN_REVIEW.getCode();
            ContractStatusDTO contractStatusDTO = new ContractStatusDTO();
            contractStatusDTO.setNeedApproval(true);
            contractStatusDTO.setMark(mark);
            dto.setContractStatus(ContractStatusEnum.PENDING.getCode());

            approve(reqVO.getContractId(), ContractNodeEnum.RENTING_OUT.getCode(), contractStatusDTO, contractRoomDO);
            settlementDO.setProcessInstanceId(contractStatusDTO.getProcessInstanceId());
        }
        ContractRoomDO updateDO = new ContractRoomDO();
        //修改 合同状态为 非正常结束
        updateDO.setId(contractRoomDO.getId());
        updateDO.setContractNode(ContractNodeEnum.RENTING_OUT.getCode());
        updateDO.setActualEndTime(reqVO.getEvictionTime());
        //换房 或者 退租不需要审批
        if (!needApproval || StringUtils.isBlank(mark)){
            settlementStatus = SettlementStatusEnum.NO_AUDIT_REQUITED.getCode();
            String contractStatus;
            //正常退租
            if (EvictionTypeEnum.NORMAL_RENTING.getCode().equals(reqVO.getEvictionType())) {
                contractStatus = ContractStatusEnum.NORMAL_END.getCode();
            } else {
                contractStatus = ContractStatusEnum.ABNORMAL_END.getCode();
            }
            dto.setContractStatus(contractStatus);
            //修改房态
            updateRoomStatus(contractRoomDO.getRoomId(), ContractNodeEnum.RENTING_OUT.getCode(), false, contractStatus,
                    contractRoomDO.getCustomerName(),contractRoomDO.getCustomerPhone(),contractRoomDO.getId(),contractRoomDO.getEndTime());

            updateDO.setContractStatus(contractStatus);

            LambdaQueryWrapper<ContractRoommateDO> qw = new LambdaQueryWrapper<>();
            qw.eq(ContractRoommateDO::getRoomContractId,contractRoomDO.getId())
                            .ne(ContractRoommateDO::getStatus,3);
            List<ContractRoommateDO> contractRoommateDOS = contractRoommateMapper.selectList(qw);
            //删除下发密码
            List<String> mobiles = contractRoommateMapper.selectByContractId(contractRoomDO.getId());
            ResultModel resultModel = iotRoomDeviceApi.clearPassword1(contractRoomDO.getRoomId(),mobiles);
            log.info("删除房间密码:{} resp:{}", contractRoomDO.getRoomId(), JSONObject.toJSONString(resultModel));
            if (CollectionUtils.isNotEmpty(contractRoommateDOS)){
                List<Long> collect = contractRoommateDOS.stream().map(ContractRoommateDO::getId).collect(Collectors.toList());
                //迁出同住人
                contractRoommateService.refundRoommateEmigration(collect);
            }

            contractRoomMapper.updateById(updateDO);
            // 续租/换房合同
            ContractRoomDO renewContract = getByFromId(contractRoomDO.getId());
            if (renewContract != null) {
                updateDO = new ContractRoomDO();
                updateDO.setId(renewContract.getId());
                updateDO.setContractNode(ContractNodeEnum.RENTING_OUT.getCode());
                updateDO.setContractStatus(contractStatus);
                updateDO.setActualEndTime(reqVO.getEvictionTime());
                contractRoomMapper.updateById(updateDO);
            }
            //修改合同未支付账单为取消
            rentOrderMapper.updatePayStatusCancel(contractRoomDO.getId());
            //查询涉及当期账单
            LocalDate evictionTime = settlementDO.getEvictionTime();
            List<Long> longs = Collections.singletonList(contractRoomDO.getId());

            //红冲
            redInvocie(evictionTime, longs);

            // 生成退租清算账单
            createCheckoutSettlementRentOrder(contractRoomDO, settlementDO, settlementVos);

            //个人用户退租推送通知
            if (CustomerTypeEnum.PERSONAL.getCode().equals(contractRoomDO.getCustomerType())) {
                //推送通知
                try {
                    pushServiceEvaluation(contractRoomDO, PushServiceEvaluationTypeEnum.RENTING_OUT.getMsg(),
                            PushServiceEvaluationTypeEnum.RENTING_OUT.getCode());
                } catch (Exception e) {
                    log.error("", e);
                }
            } else {
                //如果是最后一个合同更改大合同合同状态
                companyContractService.setCompanyStatus(contractRoomDO.getCompanyContractId(), ContractCompanyStatusEnum.RENTING_OUT.getCode());
            }
        } else {
            updateDO.setContractStatus(ContractStatusEnum.PENDING.getCode());
            updateDO.setOldContractStatus(contractRoomDO.getContractStatus());
            updateDO.setOldContractNode(contractRoomDO.getContractNode());
        }
        updateDO.setEvictionType(reqVO.getEvictionType());
        updateDO.setEvictionTime(reqVO.getEvictionTime());
        updateDO.setEvictionReason(reqVO.getEvictionReason());
        contractRoomMapper.updateById(updateDO);
        settlementDO.setClearedStatus(settlementStatus);
        dto.setContract(contractRoomDO);
        dto.setSettlementDO(settlementDO);
        dto.setList(settlementVos);
        log.info("{合同状态为：{}}", dto.getContract().getContractNode());
        return dto;
    }

    /**
     * 红冲
     * @param evictionTime 退租时间
     * @param longs 合同ids集合
     */
    @Override
    public void redInvocie(LocalDate evictionTime, List<Long> longs) {
        LambdaQueryWrapper<RentOrderDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RentOrderDO::getPayStatus,OrderPayStatusEnum.PAID.getCode())
                .in(RentOrderDO::getBusinessId, longs)
                .in(RentOrderDO::getOrderType,OrderTypeEnum.DEFAULT.getCode(), OrderTypeEnum.CUSTOM.getCode())
                .ge(RentOrderDO::getEndTime, evictionTime)
        ;
        List<RentOrderDO> rentOrderDOList = rentOrderMapper.selectList(queryWrapper);
        if(CollectionUtil.isNotEmpty(rentOrderDOList)){
            //查找到以开票的账单
            List<RentOrderDO> collect =
                    rentOrderDOList.stream().filter(f -> f.getInvoiceStatus() != null && 0!=f.getInvoiceStatus()).collect(Collectors.toList());
            if(CollectionUtil.isNotEmpty(collect)){
                log.info("红冲入参{}",JSON.toJSONString(collect));
                CommonResult<List<Long>> listCommonResult =
                        invoiceApi.invoiceRed(collect.stream().map(RentOrderDO::getId).collect(Collectors.toList()));
                if(listCommonResult.isSuccess() && CollectionUtil.isNotEmpty(listCommonResult.getData())){
                    List<Long> redOrderIds = listCommonResult.getData();
                    log.info("红冲出参{}",JSON.toJSONString(redOrderIds));
                    rentOrderMapper.update(null,new LambdaUpdateWrapper<RentOrderDO>().in(RentOrderDO::getId,redOrderIds)
                            .set(RentOrderDO::getInvoiceStatus,7)
                    );
                }
            }
            //标记不可开票订单
            List<Long> noInvoiceListIds =
                    rentOrderDOList.stream().filter(f -> f.getStartTime().compareTo(evictionTime) > 0).map(RentOrderDO::getId).collect(Collectors.toList());
            if(CollectionUtil.isNotEmpty(noInvoiceListIds)){
                rentOrderMapper.update(null,new LambdaUpdateWrapper<RentOrderDO>().in(RentOrderDO::getId,noInvoiceListIds)
                        .set(RentOrderDO::getRestrictedInvoice,1));
            }

        }
    }

    /**
     * 退租清算, 生成应收账单给租客支付
     */
    @Override
    public void createCheckoutSettlementRentOrder(
            ContractRoomDO contract,
            SettlementDO settlementDO,
            List<SettlementDetailBaseVO> detail) {
        List<SettlementDetailBaseVO> l = detail.stream().filter(f -> f.getFlowType().equals("in"))
                .collect(Collectors.toList());
        if (settlementDO.getAmount().compareTo(BigDecimal.ZERO) > 0 && "in".equals(settlementDO.getFlowType())) {
            log.info("生成退租清算{}, 生成租客账单 {}", settlementDO.getSettlementNo(), settlementDO.getAmount());
            int roommateId = 0;
            if (CustomerTypeEnum.COMPANY.getCode().equals(contract.getCustomerType())
                    && CompanyContractOrderShareTypeEnum.ALL.getCode().equals(contract.getOrderShareMethod())) {
                roommateId = 1;
            }
            LocalDate endTime = settlementDO.getEvictionTime().isBefore(LocalDate.now()) ? LocalDate.now() : settlementDO.getEvictionTime();
            RentOrderDO rentOrderDO = RentOrderDO.builder()
                    .orderType(OrderTypeEnum.CUSTOM.getCode())
                    .uuid(UUID.randomUUID().toString(true))
                    .orderNo(rentOrderService.nextRentOrderNo())
                    .num(1)
                    .totalNum(1)
                    .businessId(contract.getId())
                    .customerName(contract.getCustomerName())
                    .customerPhone(contract.getCustomerPhone())
                    .apartmentId(contract.getApartmentId())
                    .apartmentName(contract.getApartmentName())
                    .roomId(contract.getRoomId())
                    .roomName(contract.getRoomName())
                    .startTime(settlementDO.getEvictionTime())
                    .endTime(endTime)
                    .paidRentUtilities(BigDecimal.ZERO)
                    .actualPaymentAmount(BigDecimal.ZERO)
                    .totalAmount(settlementDO.getAmount())
                    .rentAmount(contract.getMonthRent())
                    .depositAmount(BigDecimal.ZERO)
                    .extrasAmount(BigDecimal.ZERO)
                    .rentDeposit(contract.getMonthRent())
                    .discountAmount(BigDecimal.ZERO)
                    .couponDiscount(BigDecimal.ZERO)
                    .rawAmount(settlementDO.getAmount())
                    .oldDepositAmount(BigDecimal.ZERO)
                    .oughtPayTime(endTime)
                    .deadlinePayTime(endTime)
                    .approvedStatus(OrderApprovedStatusEnum.NO_AUDIT_REQUITED.getCode())
                    .payOutStatus(0)
                    .memberId(roommateId == 0 ? contract.getMemberId() : null)
                    .roommateId(roommateId)
                    .build();
            rentOrderMapper.insert(rentOrderDO);
            List<OrderFeesDO> orderFeesDOList = l.stream().map(settlementDetail -> {
                OrderFeesDO orderfeesDO = new OrderFeesDO();
                orderfeesDO.setOrderId(rentOrderDO.getId());
                orderfeesDO.setFeeSort(OrderFeeSortTypeEnum.ONCE.getCode());
                orderfeesDO.setFeeId(settlementDetail.getFeeId());
                orderfeesDO.setFeeName(settlementDetail.getFeeName());
                orderfeesDO.setAmount(settlementDetail.getRefundableAmount());
                orderfeesDO.setFeeMonth(settlementDetail.getRefundableAmount());
                orderfeesDO.setStartTime(rentOrderDO.getStartTime());
                orderfeesDO.setEndTime(rentOrderDO.getEndTime());
                orderfeesDO.setDiscountAmount(BigDecimal.ZERO);
                orderfeesDO.setOrderType(rentOrderDO.getOrderType());
                orderfeesDO.setFlowType(OrderFeeFlowTypeEnum.INFLOW.getCode());
                orderfeesDO.setPaidAmount(BigDecimal.ZERO);
                orderfeesDO.setMemberId(rentOrderDO.getMemberId());
                orderfeesDO.setOperatorName(settlementDO.getOperator());
                return orderfeesDO;
            }).collect(Collectors.toList());
            orderFeesMapper.insertBatch(orderFeesDOList);
        }
    }

    private boolean isBoolean(String contractStatus) {
        //已生效、已入住、已过期 的合同可以做退租
        return ContractStatusEnum.ACTIVE.getCode().equals(contractStatus)
                || ContractStatusEnum.CHECK_IN.getCode().equals(contractStatus);
    }

    /*
     * 保存结算单
     * */
    @Transactional(rollbackFor = Exception.class)
    public void generateSettlement(SettlementDO settlementDO, List<SettlementDetailBaseVO> settlementVos) {
        settlementDO.setSettlementNo(ApiConstants.ZKHTTZ + timebaseSequenceGenerator.YYYYMMDD.next(ApiConstants.TZ, "%06d"));
        settlementMapper.insert(settlementDO);
        List<SettlementDetailDO> settlementDetailDOS = SettlementDetailConvert.INSTANCE.convert(settlementVos);
        for (SettlementDetailDO settlementDetailDO : settlementDetailDOS) {
            settlementDetailDO.setSettlementId(settlementDO.getId());
        }
        settlementDetailMapper.insertBatch(settlementDetailDOS);
    }

    private void validateExchangeCreate(ContractExchangeReqVO createReqVO) {
        // 是否有正在审核中的退租、换租、续租
        Integer renewalContractRoomDO = contractRoomMapper.getRenewalContract(createReqVO.getFromId());
        if (renewalContractRoomDO > 0) {
            throw exception(CONTRACT_NO_EXCHANGE);
        }
        // 是否存在未支付的账单
        rentOrderService.unpaidOrder(createReqVO.getContractRefundReqVO().getEvictionTime(), Collections.singletonList(createReqVO.getFromId()),
                Arrays.asList(OrderTypeEnum.DEFAULT.getCode(), OrderTypeEnum.CUSTOM.getCode()));

        //当前房间是否有正在审批 已生效的合同
        List<ContractRoomDO> roomContract = contractRoomMapper.getRoomContractByRoomId(createReqVO.getRoomId());
        if (CollectionUtils.isNotEmpty(roomContract)) {
            throw exception(EXIST_CONTRACT_NO_END);
        }

    }

    private void validateCreate(ContractCreateReqVO createReqVO) {
        if (createReqVO.getMonthRent() == null || createReqVO.getMonthRent().compareTo(BigDecimal.ZERO) == 0) {
            throw exception(MONTH_RENT_AMOUNT);
        }
        if (createReqVO.getEndTime().isBefore(createReqVO.getStartTime())) {
            throw exception(ENDTIME_BEFORE_STARTTIME);
        }

        //当前房间是否有正在审批 已生效的合同
        List<ContractRoomDO> roomContract = contractRoomMapper.getRoomContractByRoomId(createReqVO.getRoomId());
        if (CollectionUtils.isNotEmpty(roomContract)) {
            throw exception(EXIST_CONTRACT_NO_END);
        }
        CommonResult<ReserveGetApiResp> reserve = reserveApi.getByRoomId(createReqVO.getRoomId());
        //判断预定信息
        if (reserve.isSuccess()&&createReqVO.getBooking()!=null){
            ReserveGetApiResp data = reserve.getData();
            if (data==null) {
                throw exception(BOOKING);
            }
            if (!data.getBookerPhone().equals(createReqVO.getCustomerPhone())
                || !data.getBooker().equals(createReqVO.getCustomerName())) {
                throw exception(MEMBER_INCONSISTENT);
            }
            if (data.getDeposit().compareTo(createReqVO.getBooking().getBookingDeposit())!=0
                    && !RoomHouseReserveStatus.RESERVED.getCode().equals(data.getStatus()) ) {
                throw exception(BOOKING_AMOUNT);
            }
        }
    }

    @Override
    @Transactional
    public ContractRoomDO create(ContractCreateReqVO reqVO, String node) {
        //个人客户 添加租户信息 C端用户表
        MemberUserRespDTO userInfo = null;
        //如果是企业通过子合同续租的，也生成一个大合同
        if (CustomerTypeEnum.COMPANY.getCode().equals(reqVO.getCustomerType()) &&
                ContractNodeEnum.RENEWAL.getCode().equals(node)){
            CompanyContractRentDataRespVO respVO = companyContractService.get(reqVO.getCompanyContractId());
            if (respVO != null){
                BeanUtils.copyProperties(reqVO,respVO);
                CompanyContractRenewalReqVO contractRenewalReqVO = CompanyContractConvert.INSTANCE.convert5(respVO);
                contractRenewalReqVO.setRoomIds(Collections.singletonList(reqVO.getRoomId()));
                contractRenewalReqVO.setOldContractId(respVO.getId());
                contractRenewalReqVO.setIds(Collections.singletonList(reqVO.getFromId()));
                companyContractService.createRenewalContractCompany(contractRenewalReqVO);
            }
            return null;
        }
        if (CustomerTypeEnum.PERSONAL.getCode().equals(reqVO.getCustomerType())) {
            log.info("个人客户个人信息置空");
            userInfo = getMemberUser(reqVO);

        }
        log.info("用户信息,{}", JSONObject.toJSONString(userInfo));
        //设置合同状态 审批状态 签约状态
        ContractStatusDTO contractStatusDTO = setStatus(reqVO.getApartmentId(), reqVO.getContractSort(), node,reqVO.getCustomerType());
        ContractRoomDO contractRoomDO = ContractConvert.INSTANCE.convert(reqVO);
        //合同审批状态  待审批  | 审批通过 | 审批驳回
        // contractRoomDO.setApproveStatus(contractStatusDTO.getApproveStatus());
        //待甲方签约 |待乙方签约  |已签约
        contractRoomDO.setSignStatus(contractStatusDTO.getSignStatus());
        //合同状态 待审批 |  待签约 |已生效 | 正常结束 | 非正常结束 |
        contractRoomDO.setContractStatus(contractStatusDTO.getContractStatus().getCode());
        //合同节点 新签  | 续约 | 退租 | 换房
        contractRoomDO.setContractNode(node);
        contractRoomDO.setContractNo(ApiConstants.ZKZLHT + timebaseSequenceGenerator.YYYYMMDD.next(ApiConstants.CONTRACT, "%06d"));
        //签约人 默认操作人
        contractRoomDO.setSignerId(reqVO.getSignerId() == null ? SecurityFrameworkUtils.getLoginUserId() : reqVO.getSignerId());
        CommonResult<AdminUserRespDTO> result = adminUserApi.getUser(contractRoomDO.getSignerId());
        if (result.isSuccess()) {
            if (result.getData() != null) {
                contractRoomDO.setSignerName(result.getData().getNickname());
            }
        }
        //如果是企业合同设置合同状态为已生效(只有新签直接设置状态)
        if (CustomerTypeEnum.COMPANY.getCode().equals(reqVO.getCustomerType()) &&
             ContractNodeEnum.NEW.getCode().equals(node)) {
            contractRoomDO.setContractStatus(ContractStatusEnum.ACTIVE.getCode());
        }
        contractRoomDO.setMemberId(userInfo != null ? userInfo.getId() : null);
        contractRoomDO.setChangeEffectiveTime(contractStatusDTO.getChangeEffectiveTime());
        contractRoomMapper.insert(contractRoomDO);
        //原合同id
        Long fromId = reqVO.getFromId();
        //续租
        if (ContractNodeEnum.RENEWAL.getCode().equals(node) && !contractStatusDTO.getNeedApproval()) {
            log.info("无需审批的续租合同：{}", JSONObject.toJSONString(contractRoomDO));
            ContractRoomDO contractRoom = getContractRoom(fromId, LocalDate.now());
            if (contractRoom != null&& contractRoom.getEndTime().isBefore(LocalDate.now())) {
                log.info("无需审批的续租合同，且合同已到期：{}，原合同信息：{}", JSONObject.toJSONString(contractRoomDO), JSONObject.toJSONString(contractRoom));
                if (ContractSignStatusEnum.SIGNED.getCode().equals(contractRoomDO.getSignStatus())){
                    //更改续租合同状态为原合同状态
                    contractRoomDO.setContractStatus(contractRoom.getContractStatus());
                }else {
                    contractRoomDO.setContractStatus(ContractStatusEnum.NO_SIGNED.getCode());
                }
                contractRoomMapper.updateById(contractRoomDO);
                //修改原合同状态
                contractRoom.setOldContractNode(contractRoom.getContractNode());
                contractRoom.setContractNode(ContractNodeEnum.RENTING_OUT.getCode());
                contractRoom.setContractStatus(ContractStatusEnum.NORMAL_END.getCode());
                contractRoom.setActualEndTime(reqVO.getStartTime());
                contractRoomMapper.updateById(contractRoom);
                //把原合同的主住人、同住人迁过来
                contractRoommateService.roommateTransfer(fromId,contractRoomDO.getId(),contractRoom.getRoomId());
                //迁移交房数据
                contractStayService.migrateStayData(Collections.singletonList(contractRoomDO.getId()));
            }
            //个人用户退租推送通知
            if (CustomerTypeEnum.PERSONAL.getCode().equals(contractRoomDO.getCustomerType())) {
                //推送通知
                try {
                    pushServiceEvaluation(contractRoomDO, PushServiceEvaluationTypeEnum.RENEWAL.getMsg(),
                            PushServiceEvaluationTypeEnum.RENEWAL.getCode());
                } catch (Exception e) {
                    log.error("", e);
                }
            } else {
                //如果是最后一个合同更改大合同合同状态
                companyContractService.setCompanyStatus(contractRoomDO.getCompanyContractId(), ContractCompanyStatusEnum.RENEW.getCode());
            }
        }
        //换房
        if (ContractNodeEnum.EXCHANGE.getCode().equals(node)) {
            ContractRoomDO oldContractRoomDO = contractRoomMapper.selectById(fromId);
            log.info("EXCHANGE-NeedApproval:{}", contractStatusDTO.getNeedApproval());
//            if (!contractStatusDTO.getNeedApproval()) {
//                LocalDate actualEndTime = reqVO.getActualEndTime();
//                if (oldContractRoomDO != null) {
//                    oldContractRoomDO.setContractNode(ContractNodeEnum.RENTING_OUT.getCode());
//                    oldContractRoomDO.setContractStatus(ContractStatusEnum.ABNORMAL_END.getCode());
//                }
//                oldContractRoomDO.setActualEndTime(actualEndTime);
//                contractRoomMapper.updateById(oldContractRoomDO);
//                //修改原房间状态
//                updateRoomStatus(oldContractRoomDO.getRoomId(), node, false, ContractStatusEnum.ABNORMAL_END.getCode(),
//                        oldContractRoomDO.getCustomerName(),oldContractRoomDO.getCustomerPhone(),oldContractRoomDO.getId(),contractRoomDO.getEndTime());
//            }
            //换房记录落库
            contractChangingRoomsRecordService.insert(contractRoomDO, oldContractRoomDO);
        }

        //新签状态
        if(!ContractNodeEnum.RENEWAL.getCode().equals(node)){
            updateRoomStatus(contractRoomDO.getRoomId(), node, contractStatusDTO.getNeedApproval(),
                    contractStatusDTO.getContractStatus().getCode() ,
                    contractRoomDO.getCustomerName(),contractRoomDO.getCustomerPhone(),contractRoomDO.getId(),contractRoomDO.getEndTime());
        }
        //生成合同杂费数据
        addExtractData(reqVO, contractRoomDO);
        //个人合同只有新签或换房才会加同住人
        if (CustomerTypeEnum.PERSONAL.getCode().equals(reqVO.getCustomerType()) &&
                (ContractNodeEnum.NEW.getCode().equals(node) || ContractNodeEnum.EXCHANGE.getCode().equals(node) )) {
            addRoommate(reqVO, contractRoomDO);
        }
        //如果是企业子合同的新签，不用审批
        if (CustomerTypeEnum.COMPANY.getCode().equals(reqVO.getCustomerType())
            && ContractNodeEnum.NEW.getCode().equals(node)){
            return contractRoomDO;
        }
        approve(fromId, node, contractStatusDTO, contractRoomDO);
        return contractRoomDO;
    }

    private void pushServiceEvaluation(ContractRoomDO contractRoomDO,String content,Integer commentType) {
        MemberMessageDTO memberMessageDTO = new MemberMessageDTO();
        if (contractRoomDO != null){
            memberMessageDTO.setUserId(contractRoomDO.getMemberId());
            memberMessageDTO.setContent(content);
            memberMessageDTO.setType(1);
            memberMessageDTO.setStatus(1);
            memberMessageDTO.setDestination(3);
            memberMessageDTO.setCreator(contractRoomDO.getCreator());

            memberMessageDTO.setSteward(contractRoomDO.getSignerName());
            memberMessageDTO.setStewardId(contractRoomDO.getSignerId());

            memberMessageDTO.setCommentType(commentType);
            memberMessageDTO.setApartmentName(contractRoomDO.getApartmentName());
            memberMessageDTO.setRoomId(contractRoomDO.getRoomId());
            memberMessageDTO.setSignDay(contractRoomDO.getSignedTime());

            log.info("发送消息通知，消息对象：{}",JSONObject.toJSONString(memberMessageDTO));
            memberMessageApi.create(memberMessageDTO);
        }
    }

    /**
     * 查询过期的合同
     */
    public ContractRoomDO getContractRoom(Long id, LocalDate time) {
        LambdaQueryWrapper<ContractRoomDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ContractRoomDO::getId, id)
                .le(ContractRoomDO::getEndTime, time);
        return contractRoomMapper.selectOne(lambdaQueryWrapper);
    }

    public void approve(Long contractId, String contractNode, ContractStatusDTO contractStatusDTO, ContractRoomDO contractRoomDO) {
        //创建审批
        if (contractStatusDTO.getNeedApproval()) {
            String processInstance = createProcessInstance(contractStatusDTO, contractRoomDO);
            // 保存流程实例id
            ContractRoomDO contractRoomDO1 = new ContractRoomDO();
            contractRoomDO1.setId(contractRoomDO.getId());
            contractRoomDO1.setProcessInstance(processInstance);
            contractRoomMapper.updateById(contractRoomDO1);
            contractStatusDTO.setProcessInstanceId(processInstance);

            ContractActiveAssociationDO contractActiveAssociationDO = new ContractActiveAssociationDO();
            contractActiveAssociationDO.setType(BpmBusinessTypeEnum.CONTRACT.name());
            contractActiveAssociationDO.setApproveType(ContractApproveTypeEnum.getEnum(contractNode).getBusinessKey());
            contractActiveAssociationDO.setBusinessId(contractRoomDO.getId());
            contractActiveAssociationDO.setProcessInstance(processInstance);
            contractActiveAssociationMapper.insert(contractActiveAssociationDO);
        } else {
            //todo 不要审批  合同已经 到期 修改原合同为 正常结束
            if (ContractNodeEnum.RENEWAL.getCode().equals(contractNode)) {
                ContractRoomDO roomContractById = contractRoomMapper.getRoomContractById(contractId);
                //是否过期
                boolean flag = roomContractById.getEndTime().isBefore(LocalDate.now());
                roomContractById.setOldContractStatus(roomContractById.getContractStatus());
                roomContractById.setOldContractNode(roomContractById.getContractNode());
                if (flag) {
                    roomContractById.setContractStatus(ContractStatusEnum.NORMAL_END.getCode());
                    roomContractById.setContractNode(ContractNodeEnum.RENTING_OUT.getCode());
                    contractRoomMapper.updateById(roomContractById);
                }
            }
            if(ContractSignStatusEnum.SIGNED.getCode().equals(contractRoomDO.getSignStatus()) ){
                //发送 预定信息
                try {
                    log.error("预定信息：{}，memberId：{}", contractRoomDO.getRoomId(), contractRoomDO.getMemberId());
                    reserveApi.signSuccess(contractRoomDO.getRoomId(), contractRoomDO.getMemberId());
                } catch (Exception e) {
                    log.error("不需要审批，参数roomId：{}，memberId：{}，异常信息：", contractRoomDO.getRoomId(), contractRoomDO.getMemberId());
                }
            }

        }
        // 判断是否新签
        sendNewSignSystemEvent(contractRoomDO);
    }

    @Override
    public ContractRoomDO getByFromId(Long contractId) {
        LambdaQueryWrapper<ContractRoomDO> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ContractRoomDO::getFromId,contractId)
                .in(ContractRoomDO::getContractStatus,
                        Arrays.asList(ContractStatusEnum.PENDING.getCode(),ContractStatusEnum.NO_SIGNED.getCode(),
                                ContractStatusEnum.ACTIVE.getCode(),ContractStatusEnum.CHECK_IN.getCode()));
        return contractRoomMapper.selectOne(lambdaQueryWrapper);
    }

    /**
     * 根据memberid查询历史有效合同
     *
     * @param memberId
     * @return
     */
    @Override
    public List<ContractRoomDO> getHistoryEffectiveContractByMemberId(Long memberId) {
        List<ContractRoomDO> list = contractRoomMapper.selectList("member_id", memberId);
        return list.stream()
                .filter(c -> ContractStatusEnum.ACTIVE.getCode().equals(c.getContractStatus()) ||
                        ContractStatusEnum.CHECK_IN.getCode().equals(c.getContractStatus()) ||
                        ContractStatusEnum.NORMAL_END.getCode().equals(c.getContractStatus()) ||
                        ContractStatusEnum.ABNORMAL_END.getCode().equals(c.getContractStatus())).collect(Collectors.toList());
    }

    @Override
    public RoomContractVO getContract(HomeQueryVO req) {
        IPage<T> mpPage = MyBatisUtils.buildPage(req);
        IPage<RoomContractRespVO> contract = contractRoomMapper.getContract(mpPage, req.getStartDate(),req.getApartmentId());
        List<RoomContractRespVO> records = contract.getRecords();
        for (RoomContractRespVO record : records) {
            if(ContractStatusEnum.NO_SIGNED.getCode().equals(record.getContractStatus())) {
                if(ContractSignStatusEnum.SIGNED_PARTY_A.getCode().equals(record.getSignStatus())){
                    record.setContractStatusName("待签约（甲方）");
                }else if (ContractSignStatusEnum.SIGNED_PARTY_B.getCode().equals(record.getSignStatus())){
                    record.setContractStatusName("待签约（乙方）");
                }
            }else {
                if(record.getEndTime().isBefore(LocalDate.now())) {
                    long days = ChronoUnit.DAYS.between(record.getEndTime(),LocalDate.now());
                    record.setContractStatusName("逾期" + days + "天");
                }else {
                    record.setContractStatusName("已生效");
                }
            }
        }
        PageResult<RoomContractRespVO> pageResult=new PageResult<>();
        pageResult.setList(records);
        pageResult.setTotal(contract.getTotal());
        HomeData homeData = homeService.getHomeData(req);
        RoomContractVO roomContractVO=new RoomContractVO();
        roomContractVO.setRoomContractResp(pageResult);
        roomContractVO.setHomeData(homeData);

        return roomContractVO;
    }

    private String createProcessInstance(ContractStatusDTO contractStatusDTO, ContractRoomDO contractRoomDO) {
        // 工作流参数
        BpmProcessInstanceCreateReqDTO bpmProcessInstanceCreateReqDTO = new BpmProcessInstanceCreateReqDTO();
        bpmProcessInstanceCreateReqDTO.setProcessDefinitionKey(contractStatusDTO.getMark());
        bpmProcessInstanceCreateReqDTO.setBusinessType((contractRoomDO.getCustomerCompanyId() != null && contractRoomDO.getCustomerCompanyId() > 0)
                ? BpmBusinessTypeEnum.COMPANY_CONTRACT.name() : BpmBusinessTypeEnum.CONTRACT.name());
        bpmProcessInstanceCreateReqDTO.setBusinessKey(String.valueOf(contractRoomDO.getId()));
        Map<String, Object> variables = new HashMap<>();
        variables.put("customerName", contractRoomDO.getCustomerName());
        variables.put("customerPhone", contractRoomDO.getCustomerPhone());
        variables.put("roomName", contractRoomDO.getApartmentName() + contractRoomDO.getRoomName());
        variables.put("roomId", contractRoomDO.getRoomId());
        variables.put("createName", contractRoomDO.getSignerName());
        if (bpmProcessInstanceCreateReqDTO.getBusinessType().equals(BpmBusinessTypeEnum.COMPANY_CONTRACT.name())) {
            variables.put("companyId", contractRoomDO.getCustomerCompanyId());
            variables.put("companyContractId", contractRoomDO.getCompanyContractId());
        }
        variables.put("contractStartDate", contractRoomDO.getStartTime().format(DateTimeFormatter.ISO_DATE));
        variables.put("contractEndDate", contractRoomDO.getEndTime().format(DateTimeFormatter.ISO_DATE));
        bpmProcessInstanceCreateReqDTO.setVariables(variables);
        bpmProcessInstanceCreateReqDTO.setTenantId(getLoginUser().getTenantId());
        bpmProcessInstanceCreateReqDTO.setServiceName(applicationName);

        BpmProcessInstanceCreateReqDTO.ExtendDto extendDto = new BpmProcessInstanceCreateReqDTO.ExtendDto();
        extendDto.setRoomId(Objects.nonNull(contractRoomDO.getRoomId())?contractRoomDO.getRoomId().toString():"");
        extendDto.setContractId(contractRoomDO.getId());
        extendDto.setContractNo(contractRoomDO.getContractNo());
        extendDto.setRoomName(contractRoomDO.getRoomName());
        extendDto.setApartmentId(contractRoomDO.getApartmentId());
        extendDto.setApartmentName(contractRoomDO.getApartmentName());
        extendDto.setCustomName(contractRoomDO.getCustomerName());
        extendDto.setCustomPhone(contractRoomDO.getCustomerPhone());
        bpmProcessInstanceCreateReqDTO.setExtendDto(extendDto);

        CommonResult<String> processInstance = bpmProcessInstanceApi.createProcessInstance(getLoginUserId(), bpmProcessInstanceCreateReqDTO);
        log.info("processInstance:{}", JsonUtils.toJsonString(processInstance));
        return processInstance.getCheckedData();
    }

    @NotNull
    private MemberUserRespDTO getMemberUser(ContractCreateReqVO reqVO) {
        MemberUserReqDTO memberUserReqDTO = new MemberUserReqDTO();
        memberUserReqDTO.setMobile(reqVO.getCustomerPhone());
        memberUserReqDTO.setName(reqVO.getCustomerName());
        memberUserReqDTO.setCompanyId(reqVO.getCustomerCompanyId());
        memberUserReqDTO.setCardNo(reqVO.getCustomerIdNumber());
        //紧急联系人
        EmergencyContact emergencyContact=new EmergencyContact();
        emergencyContact.setName(reqVO.getEmergencyContactName());
        emergencyContact.setMobile(reqVO.getEmergencyContactPhone());
        emergencyContact.setRelationShip(reqVO.getEmergencyContactRelationship());
        List<EmergencyContact> emergencyContacts =new ArrayList<>();
        emergencyContacts.add(emergencyContact);
        memberUserReqDTO.setEmergencyContacts(emergencyContacts);
        if (StringUtils.isNotBlank(reqVO.getCustomerEducation())){
            memberUserReqDTO.setEducation(Integer.valueOf(reqVO.getCustomerEducation()));
        }
        memberUserReqDTO.setAddress(reqVO.getRenterAddress());
        memberUserReqDTO.setEmail(reqVO.getRenterEmail());
        CommonResult<MemberUserRespDTO> userInfo = memberUserApi.getUserInfo(memberUserReqDTO);
        if (userInfo.getCode() != 0) {
            throw exception(MEMBER_NOT_EXISTS);
        }
        return userInfo.getData();
    }
    @NotNull
    private MemberUserRespDTO getMemberUser(String phone, String name, Long companyId, String cardNo) {
        MemberUserReqDTO memberUserReqDTO = new MemberUserReqDTO();
        memberUserReqDTO.setMobile(phone);
        memberUserReqDTO.setName(name);
        memberUserReqDTO.setCompanyId(companyId);
        memberUserReqDTO.setCardNo(cardNo);
        CommonResult<MemberUserRespDTO> userInfo = memberUserApi.getUserInfo(memberUserReqDTO);
        if (userInfo.getCode() != 0) {
            throw exception(MEMBER_NOT_EXISTS);
        }
        return userInfo.getData();
    }


    public ContractStatusDTO setStatus(Long apartmentId, String contractSort, String node ,String customerType) {
        ContractStatusDTO contractStatusDTO = new ContractStatusDTO();
        if (ContractNodeEnum.NEW.getCode().equals(node)) {
            //审批流类
            contractStatusDTO.setType(ApproveTypeEnum.CONTRACT.getMsg());
        } else if (ContractNodeEnum.RENEWAL.getCode().equals(node)) {
            //判断是否是企业子合同
            if (CustomerTypeEnum.COMPANY.getCode().equals(customerType)){
                contractStatusDTO.setType(ApproveTypeEnum.RENEWAL_COMPANY_CONTRACT.getMsg());
            }else{
                contractStatusDTO.setType(ApproveTypeEnum.RENEWAL_CONTRACT.getMsg());
            }
        } else if (ContractNodeEnum.EXCHANGE.getCode().equals(node)) {
            contractStatusDTO.setType(ApproveTypeEnum.EXCHANGE_CONTRACT.getMsg());
        }
        String mark = contractApprovalService.getByRoomIdAndMark(apartmentId, contractStatusDTO.getType());
        //需要审批
        if (StringUtils.isNotBlank(mark)) {
            //需要审核  审批状态变成待审批  合同状态变成待审批
            contractStatusDTO.setContractStatus(ContractStatusEnum.PENDING);
            contractStatusDTO.setNeedApproval(Boolean.TRUE);
            contractStatusDTO.setMark(mark);
        }
        //不需要审批 需要电子签约
        if (StringUtils.isBlank(mark) && signStatus(contractSort)) {
            //合同状态 待签约
            contractStatusDTO.setContractStatus(ContractStatusEnum.NO_SIGNED);
        }
        //不需要审批 不需要电子签约
        if (StringUtils.isBlank(mark) && !signStatus(contractSort)) {
            contractStatusDTO.setChangeEffectiveTime(LocalDateTime.now());
            contractStatusDTO.setContractStatus(ContractStatusEnum.ACTIVE);
        }
        if (signStatus(contractSort)) {
            //签约状态 待甲方签约
            contractStatusDTO.setSignStatus(ContractSignStatusEnum.SIGNED_PARTY_A.getCode());
        }
        log.info("contractStatusDTO:{}", JSON.toJSONString(contractStatusDTO));
        return contractStatusDTO;
    }

    //判断 是否需要电子签约
    private static boolean signStatus(String contractSort) {
        return ContractTypeEnum.ELECTRONIC.getCode().equals(contractSort);
    }

    /**
     * 更新同住人
     */
    private void resetRoommate(ContractCreateReqVO createReqVO, ContractRoomDO contractRoomDO) {
        contractRoommateMapper.deleteByContractId(contractRoomDO.getId());
        addRoommate(createReqVO, contractRoomDO);
    }

    /**
     * 保存同住人信息
     *
     * @param createReqVO
     * @param contractRoomDO
     */
    private void addRoommate(ContractCreateReqVO createReqVO, ContractRoomDO contractRoomDO) {
        // 主住人
        ContractRoommateDO contractRoommateDO =
                ContractRoommateDO.builder()
                        .roomId(contractRoomDO.getRoomId())
                        .roomContractId(contractRoomDO.getId())
                        .roommateName(contractRoomDO.getCustomerName())
                        .roommatePhone(contractRoomDO.getCustomerPhone())
                        .roommateIdType(contractRoomDO.getCustomerIdType())
                        .roommateIdNumber(contractRoomDO.getCustomerIdNumber())
                        .nationality(contractRoomDO.getCustomerNationality())
                        .operator(SecurityFrameworkUtils.getLoginUserId())
                        .roommateType(1)
                        .memberId(contractRoomDO.getMemberId())
                        .build();

        if (CollectionUtils.isEmpty(createReqVO.getRoommateReqVOList())) {
            contractRoommateMapper.insert(contractRoommateDO);
            return;
        }
        // 同住人
        List<ContractRoommateDO> roommate = ContractRoommateConvert.INSTANCE.convertList02(createReqVO.getRoommateReqVOList());
        for (ContractRoommateDO roommateDO : roommate) {
            MemberUserRespDTO roommateDto = getMemberUser(roommateDO.getRoommatePhone(), roommateDO.getRoommateName(),
                    createReqVO.getCustomerCompanyId(), roommateDO.getRoommateIdNumber());
            roommateDO.setMemberId(roommateDto.getId());
            roommateDO.setRoomId(contractRoomDO.getRoomId());
            roommateDO.setRoomContractId(contractRoomDO.getId());
        }

        roommate.add(contractRoommateDO);
        contractRoommateMapper.insertBatch(roommate);
    }

    /**
     * 重置杂费信息
     *
     * @param createReqVO
     * @param contractRoomDO
     */
    private void resetExtractDate(ContractCreateReqVO createReqVO, ContractRoomDO contractRoomDO) {
        contractExtrasDataService.deleteByContractId(contractRoomDO.getId());
        addExtractData(createReqVO, contractRoomDO);
    }

    /**
     * 保存杂费信息
     *
     * @param createReqVO
     * @param contractRoomDO
     */
    private void addExtractData(ContractCreateReqVO createReqVO, ContractRoomDO contractRoomDO) {
        if (CollectionUtils.isEmpty(createReqVO.getOrderFeesVo())) {
            return;
        }
        //合同添加杂费
        List<ContractExtrasDataDO> contractExtrasDataDOList = createReqVO.getOrderFeesVo()
                .stream().map(item -> ContractExtrasDataDO.builder()
                        .contractId(contractRoomDO.getId())
                        .feeSort(item.getFeeSort())
                        .feeId(item.getFeeId())
                        .feeName(item.getFeeName())
                        .amount(item.getFeeAmount())
                        .payMethodF(contractRoomDO.getPayMethodF())
                        .build()
                ).collect(Collectors.toList());

        contractExtrasDataService.createExtrasDataList(contractExtrasDataDOList);
    }

    //账单生成
    @Transactional(rollbackFor = Exception.class)
    public void createBill(RentOrderVO rentOrderVO, OrderResp orderResp) {
        List<OrderDetailVo> billsList = orderResp.getOrderDetailVos();
        //账单承担方 设置；
        billsList = setHold(rentOrderVO, billsList);
        rentOrderVO.setOrderType(OrderTypeEnum.DEFAULT.getCode());
        rentOrderVO.setApprovedStatus(OrderApprovedStatusEnum.NO_AUDIT_REQUITED.getCode());
        //首期账单 抵扣
//        setFirstBillPid(rentOrderVO, firstBillPid, billsList);
        //创建账单
        List<RentOrderDO> rentorder = rentOrderService.createBatchRentOrder(billsList, rentOrderVO);

        //创建账单明细
        orderFeesService.createBatchOrderFees(rentorder);

    }

    /*
     * 首期账单抵扣设置
     * */
    private static void setFirstBillPid(RentOrderVO rentOrderVO, BigDecimal firstBillPid, List<OrderDetailVo> billsList) {
        if (firstBillPid.compareTo(BigDecimal.ZERO) == 0) {
            return;
        }
        for (OrderDetailVo item : billsList) {
            if (item.getStartTime().equals(rentOrderVO.getStartTime())) {
                // 抵扣金额 和 首期账单金额一致
                if (item.getTotalAmount().equals(firstBillPid)) {
                    item.setPayStatus(OrderPayStatusEnum.PAID.getCode());
                    item.setPaidRentUtilities(item.getTotalAmount());
                    for (OrderFeesCreateReqVO orderFeesCreateReqVO : item.getFeeDetail()) {
                        orderFeesCreateReqVO.setPayStatus(OrderPayStatusEnum.PAID.getCode());
                        orderFeesCreateReqVO.setPaidAmount(orderFeesCreateReqVO.getAmount());
                    }
                } else if (item.getTotalAmount().compareTo(firstBillPid) > 0) {
                    item.setPayStatus(OrderPayStatusEnum.PART.getCode());
                    item.setPaidRentUtilities(item.getTotalAmount());
                    //剩余已支付金额
                    BigDecimal residue = BigDecimal.ZERO;
                    //累计金额
                    BigDecimal accumulate = BigDecimal.ZERO;

                    for (OrderFeesCreateReqVO orderFeesCreateReqVO : item.getFeeDetail()) {
                        accumulate = accumulate.add(orderFeesCreateReqVO.getAmount());
                        residue = firstBillPid.subtract(orderFeesCreateReqVO.getAmount());

                        if (firstBillPid.compareTo(accumulate) >= 0) {
                            orderFeesCreateReqVO.setPayStatus(OrderPayStatusEnum.PAID.getCode());
                            orderFeesCreateReqVO.setPaidAmount(orderFeesCreateReqVO.getAmount());
                        } else {
                            if (accumulate.compareTo(firstBillPid) > 0) {
                                orderFeesCreateReqVO.setPayStatus(OrderPayStatusEnum.PART.getCode());
                                orderFeesCreateReqVO.setPaidAmount(residue);
                            }
                            break;
                        }
                    }
                } else {
                    throw exception(AMOUNT_INCONSISTENT);
                }
            }
        }
    }

    private static List<OrderDetailVo> setHold(RentOrderVO rentOrderVO, List<OrderDetailVo> billsList) {
        //个人合同
        if (CustomerTypeEnum.PERSONAL.getCode().equals(rentOrderVO.getCustomerType())) {
            for (OrderDetailVo orderDetailVo : billsList) {
                orderDetailVo.setRoommateId(0);
            }
            return billsList;
        }
        //企业承担
        if (CustomerTypeEnum.COMPANY.getCode().equals(rentOrderVO.getCustomerType())) {
            for (OrderDetailVo orderDetailVo : billsList) {
                orderDetailVo.setRoommateId(1);
            }
            return billsList;
        }
        /*log.info("=================企业承担全部生成账单信息：============{}================",JSON.toJSONString(billsList));
        List<OrderDetailVo> bill = new ArrayList<>();
        //企业承担租金押金
        if (Objects.equals(CompanyContractOrderShareTypeEnum.ENTERPRISE_ONLY_BEARS_THE_RENT_DEPOSIT.getCode(), rentOrderVO.getOrderShareMethod())) {
            billsList.forEach(item -> {
                OrderFeesCreateReqVO rentFee = item.getFeeDetail().stream().filter(f -> ApiConstants.RENT_FEE_ID.equals(f.getFeeId())).findFirst().orElse(null);
                OrderFeesCreateReqVO deposit = item.getFeeDetail().stream().filter(f -> ApiConstants.DEPOSIT_FEE_ID.equals(f.getFeeId())).findFirst().orElse(null);

                OrderDetailVo orderDetailVo = new OrderDetailVo();
                if (rentFee != null) {
                    orderDetailVo.setRentAmount(rentFee.getAmount());
                    orderDetailVo.setDiscountValue(rentFee.getDiscountAmount());
                    orderDetailVo.setTotalAmount(rentFee.getAmount().subtract(rentFee.getDiscountAmount()));
                    item.getFeeDetail().remove(rentFee);
                }
                if (deposit != null) {
                    orderDetailVo.setDepositAmount(deposit.getAmount());
                    item.getFeeDetail().remove(deposit);
                }
                orderDetailVo.setPaidRentUtilities(Stream.of(rentFee, deposit)
                        .filter(Objects::nonNull).map(OrderFeesBaseVO::getPaidAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
                orderDetailVo.setStartTime(item.getStartTime());
                orderDetailVo.setEndTime(item.getEndTime());
                orderDetailVo.setRoommateId(1);
                orderDetailVo.setTotalAmount(orderDetailVo.getRentAmount().add(orderDetailVo.getDepositAmount().subtract(orderDetailVo.getDiscountValue())));
                orderDetailVo.setFeeDetail(deposit == null ? Collections.singletonList(rentFee) : Arrays.asList(rentFee, deposit));
                orderDetailVo.setReceivablesTime(item.getReceivablesTime());
                log.info("=================企业承担租金押金过滤账单设置账单orderDetailVo：============{}================",JSON.toJSONString(orderDetailVo));
                bill.add(orderDetailVo);

                //除租金押金外 设置租客承担
                List<OrderFeesCreateReqVO> feeList = item.getFeeDetail().stream().filter(f -> f.getAmount().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(feeList)) {
                    OrderDetailVo feeVO = new OrderDetailVo();
                    for (OrderFeesCreateReqVO rent : feeList) {
                        feeVO.setExtrasAmount(feeVO.getExtrasAmount().add(rent.getAmount()));
                        feeVO.setExtraDiscountValue(feeVO.getExtraDiscountValue().add(rent.getDiscountAmount()));
                        if (Objects.nonNull(rent.getPaidAmount())) {
                            if(feeVO.getPaidRentUtilities() == null){
                                feeVO.setPaidRentUtilities(BigDecimal.ZERO);
                            }
                            feeVO.setPaidRentUtilities(feeVO.getPaidRentUtilities().add(rent.getPaidAmount()));
                        }
                    }
                    feeVO.setStartTime(item.getStartTime());
                    feeVO.setEndTime(item.getEndTime());
                    feeVO.setRoommateId(0);
                    feeVO.setTotalAmount(feeVO.getExtrasAmount());
                    feeVO.setDiscountValue(feeVO.getExtraDiscountValue());
                    feeVO.setFeeDetail(feeList);
                    feeVO.setReceivablesTime(item.getReceivablesTime());
                    log.info("=================除租金押金外 设置租客承担feeVO：============{}================",JSON.toJSONString(feeVO));
                    bill.add(feeVO);
                }
            });
        }
        log.info("=================企业承担租金押金生成账单信息：============{}================",JSON.toJSONString(bill));*/
        return null;
    }

    @Override
    public void update(ContractUpdateReqVO updateReqVO) {
        ContractRoomDO contract = null;
        if (updateReqVO.getId() != null) {
            contract = contractRoomMapper.selectById(updateReqVO.getId());
        } else if (StringUtils.isNotBlank(updateReqVO.getContractNo())) {
            contract = contractRoomMapper.selectOne("contract_no", updateReqVO.getContractNo());
        }
        if (contract == null) {
            throw new ServiceException(CONTRACT_NOT_EXISTS);
        }
        try (DistributeLock lock = getLock(contract.getId())) {
            if (!lock.tryLock()) {
                throw new ServiceException(CONTRACT_CONCURRENT_MODIFY);
            }
            // 只有新签审批驳回可以编辑
            if (!ContractNodeEnum.NEW.getCode().equals(contract.getContractNode())
                    && !ContractStatusEnum.REJECT.getCode().equals(contract.getContractStatus())) {
                throw new ServiceException(CONTRACT_CANNOT_MODIFY);
            }
            // 根据账单计算账单金额
            RentOrderVO rentOrderVO = ContractConvert.INSTANCE.convertToRentOrder(updateReqVO);

            OrderResp orderResp = new OrderResp();
            if (Objects.nonNull(rentOrderVO.getBillRules()) && rentOrderVO.getBillRules() == 1){
                // 自然月生成账单
                orderResp = DateRangeGenerator.calculateNaturalMonthBillAmount(rentOrderVO);
            }else {
                // 按非自然月生成账单
                // 根据账单计算账单金额
                orderResp = DateRangeGenerator.calculateBillAmount(rentOrderVO);
            }

            updateReqVO.setTotalRent(orderResp.getTotalRent());
            // 合同状态
            ContractStatusDTO contractStatusDTO = setStatus(updateReqVO.getApartmentId(), updateReqVO.getContractSort(),
                     contract.getContractNode(),updateReqVO.getCustomerType());
            ContractRoomDO update = ContractConvert.INSTANCE.convert(updateReqVO);
            update.setId(contract.getId());
            update.setRoomId(contract.getRoomId());
            update.setContractNode(contract.getContractNode());
            update.setSignStatus(contractStatusDTO.getSignStatus());
            update.setContractStatus(contractStatusDTO.getContractStatus().getCode());
            update.setSignerId(SecurityFrameworkUtils.getLoginUserId());
            update.setMemberId(contract.getMemberId());
            update.setCustomerName(contract.getCustomerName());
            update.setCustomerPhone(contract.getCustomerPhone());
            update.setCustomerIdType(contract.getCustomerIdType());
            update.setCustomerIdNumber(contract.getCustomerIdNumber());
            update.setCustomerNationality(contract.getCustomerNationality());
            CommonResult<AdminUserRespDTO> result = adminUserApi.getUser(update.getSignerId());
            if (result.isSuccess()) {
                if (result.getData()!=null) {
                    update.setSignerName(result.getData().getNickname());
                    rentOrderVO.setOperatorName(result.getData().getNickname());
                }
            }
            rentOrderVO.setContractId(contract.getId());
            rentOrderVO.setMemberId(contract.getMemberId());
            rentOrderVO.setOrderFeesVo(updateReqVO.getOrderFeesVo());

            ContractRoomDO finalContract = contract;

            OrderResp finalOrderResp = orderResp;

            transactionTemplate.executeWithoutResult(status -> {
                // 删除原账单
                rentOrderService.deleteOrderByBusinessId(update.getId(), OrderTypeEnum.DEFAULT.getCode());
                //生成 账单集合
                createBill(rentOrderVO, finalOrderResp);

                // 重置同住人数据
                resetRoommate(updateReqVO, update);
                // 重置杂费数据
                resetExtractDate(updateReqVO, update);
                // 更新房态
                updateRoomStatus(update.getRoomId(), update.getContractNode(), contractStatusDTO.getNeedApproval(),
                        contractStatusDTO.getContractStatus().getCode(),
                        update.getCustomerName(),update.getCustomerPhone(),update.getId(), finalContract.getEndTime());

                // 发起流程
                if (contractStatusDTO.getNeedApproval()) {
                    String processInstanceId = createProcessInstance(contractStatusDTO, update);
                    update.setProcessInstance(processInstanceId);
                    ContractActiveAssociationDO contractActiveAssociationDO = new ContractActiveAssociationDO();
                    contractActiveAssociationDO.setType(BpmBusinessTypeEnum.CONTRACT.name());
                    contractActiveAssociationDO.setApproveType(ContractApproveTypeEnum.getEnum(update.getContractNode()).getBusinessKey());
                    contractActiveAssociationDO.setBusinessId(update.getId());
                    contractActiveAssociationDO.setProcessInstance(update.getProcessInstance());
                    contractActiveAssociationMapper.insert(contractActiveAssociationDO);
                }
                contractRoomMapper.updateById(update);
            });
        }
    }

    @Override
    public void update(ContractUpdateStatusVO req) {
        // 校验存在
//        validateExists(req.getId());
        // 更新
        ContractRoomDO updateObj = ContractConvert.INSTANCE.convert(req);
        contractRoomMapper.updateById(updateObj);
    }

    @Override
    public void delete(Long id) {
        // 校验存在
        validateExists(id);
        // 删除
        contractRoomMapper.deleteById(id);
    }

    private ContractRoomDO validateExists(Long id) {
        ContractRoomDO contractRoomDO = contractRoomMapper.selectById(id);
        if (contractRoomDO == null) {
            throw exception(CONTRACT_NOT_EXISTS);
        }
        return contractRoomDO;
    }

    @Override
    public ContractRoomDO get(Long id) {
        return contractRoomMapper.selectById(id);
    }

    @Override
    public List<ContractRoomDO> getList(Collection<Long> ids) {
        return contractRoomMapper.selectBatchIds(ids);
    }

    @Override
    public RoomContractRespVO getRoomContract(Long id) {
        QueryWrapper<ContractRoomDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ContractRoomDO::getRoomId, id);
        //合同状态在（状态是非结束状态非失效，不是续租）or（状态是非结束状态非失效，不是审批状态，是续租，在周期内）
        queryWrapper.lambda()
                .and(qw ->
                        qw.and(qw2 -> qw2.notIn(ContractRoomDO::getContractStatus,
                                Arrays.asList(ContractStatusEnum.NORMAL_END.getCode(),
                                        ContractStatusEnum.ABNORMAL_END.getCode(),
                                        ContractStatusEnum.LOST_EFFECTIVENESS.getCode()))
                                .ne(ContractRoomDO::getContractNode, ContractNodeEnum.RENEWAL.getCode()))
                                .or(qw1 -> qw1.eq(ContractRoomDO::getContractNode, ContractNodeEnum.RENEWAL.getCode())
                                        .notIn(ContractRoomDO::getContractStatus,
                                                Arrays.asList(ContractStatusEnum.NORMAL_END.getCode(),
                                                        ContractStatusEnum.ABNORMAL_END.getCode(),
                                                        ContractStatusEnum.LOST_EFFECTIVENESS.getCode(),
                                                        ContractStatusEnum.PENDING.getCode()))))
                .orderByDesc(ContractRoomDO::getId)
                .orderByAsc(ContractRoomDO::getStartTime);
        List<ContractRoomDO> contractRoomDOS = contractRoomMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(contractRoomDOS)){
            return null;
        }
        log.info("======================{}",JSON.toJSONString(contractRoomDOS));
        ContractRoomDO contractDO = contractRoomDOS.stream().filter(c -> !ContractStatusEnum.REJECT.getCode().equals(c.getContractStatus()))
                .filter(c -> LocalDate.now().compareTo(c.getStartTime()) >= 0)
                .findFirst().orElseGet(() -> null);
        if(ObjectUtil.isNull(contractDO)){
            return null;
        }
        RoomContractRespVO roomContractRespVO = ContractConvert.INSTANCE.convert1(contractDO);

        if (Objects.nonNull(contractDO)) {
            //备案状态不为空
            if(ObjectUtil.isNotNull(roomContractRespVO.getRecordStatus())){
                //已备案
                RecordStatusEnum.registered.stream().filter(fb->fb.compareTo(roomContractRespVO.getRecordStatus())==0).findFirst().ifPresent(p-> {
                    roomContractRespVO.setRecordStatus(ApiConstants.REGISTERED);
                });
                //未备案
                RecordStatusEnum.unRegistered.stream().filter(fb->fb.compareTo(roomContractRespVO.getRecordStatus())==0).findFirst().ifPresent(p-> {
                    roomContractRespVO.setRecordStatus(ApiConstants.UN_REGISTERED);
                });
            }
            //通过创建人id获取合同发起人
            if (contractDO.getCreator() != null) {
                CommonResult<AdminUserRespDTO> user = adminUserApi.getUser(Long.parseLong(contractDO.getCreator()));
                if (user.getData() != null) {
                    String nickname = user.getData().getNickname();
                    roomContractRespVO.setOriginatorName(nickname);
                }
            }

            //获取房间租金，用于续租
            CommonResult<RoomHouseRespVo> room = roomHouseApi.getRoomHouseinfoById(contractDO.getRoomId());
            if (room.getData() != null) {
                RoomHouseRespVo data = room.getData();
                if (data.getMonthRental() != null) {
                    roomContractRespVO.setPrice(BigDecimal.valueOf(data.getMonthRental()));
                }
                if (StringUtils.isNotBlank(data.getFullRoomName())) {
                    roomContractRespVO.setFullRoomName(data.getFullRoomName());
                }
                StringBuffer roomAddress = new StringBuffer();
                roomAddress.append(StringUtils.isNotBlank(data.getProvince())?data.getProvince():"");
                roomAddress.append(StringUtils.isNotBlank(data.getCity())?data.getCity():"");
                roomAddress.append(StringUtils.isNotBlank(data.getDistrict())?data.getDistrict():"");
                roomAddress.append(StringUtils.isNotBlank(data.getStreet())?data.getStreet():"");
                roomAddress.append(StringUtils.isNotBlank(data.getAddress())?data.getAddress():"");
                roomContractRespVO.setRoomAddress(roomAddress.toString());
            }
        }
        //转换合同状态
        if (roomContractRespVO != null) {
            ContractStatusVO contractStatusVO = transitionStatus(roomContractRespVO.getContractNode(),
                    roomContractRespVO.getContractStatus(), roomContractRespVO.getSignStatus(), roomContractRespVO.getEndTime());

            roomContractRespVO.setContractStatusName(contractStatusVO.getContractStatusName());
            roomContractRespVO.setContractCode(contractStatusVO.getContractCode());
        }
        addCustomerCompany(null, roomContractRespVO);
        return roomContractRespVO;
    }


    @Override
    public List<RoomContractListVO> getRoomContractListByRoomContractType(Long roomId, Integer roomContractType) {
        List<RoomContractListVO> contractList = null;
        QueryWrapper<ContractRoomDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ContractRoomDO::getRoomId, roomId);
        switch (Objects.requireNonNull(ContractStatusTypeEnum.getByCode(roomContractType))) {
            case RENEWAL:
                // 续租合同查询状态（合同类型为续租，且合同开始时间大于当前时间,状态为已生效）or （状态为待审批）
                queryWrapper.lambda().and(qw ->
                        qw.and(qw1 -> qw1.eq(ContractRoomDO::getContractNode, ContractNodeEnum.RENEWAL.getCode())
                                .gt(ContractRoomDO::getStartTime, LocalDate.now())
                                .eq(ContractRoomDO::getContractStatus, ContractStatusEnum.ACTIVE.getCode()))
                                .or(qw1 -> qw1.eq(ContractRoomDO::getContractNode, ContractNodeEnum.RENEWAL.getCode())
                                        .in(ContractRoomDO::getContractStatus, Arrays.asList(ContractStatusEnum.PENDING.getCode(), ContractStatusEnum.NO_SIGNED.getCode())))
                );
                contractList = ContractConvert.INSTANCE.convertList03(contractRoomMapper.selectList(queryWrapper));
                break;
            case HISTORY:
                // 历史合同查询状态（都是结束状态的）
                queryWrapper.lambda().and(qw ->
                        qw.and(qw1 -> qw1.in(ContractRoomDO::getContractStatus,
                                Arrays.asList(ContractStatusEnum.NORMAL_END.getCode(),
                                        ContractStatusEnum.ABNORMAL_END.getCode(),
                                        ContractStatusEnum.LOST_EFFECTIVENESS.getCode())))
                                .or(qw1 -> qw1.eq(ContractRoomDO::getContractNode, ContractNodeEnum.RENEWAL.getCode())
                                        .eq(ContractRoomDO::getContractStatus, ContractStatusEnum.REJECT.getCode()))
                );
                queryWrapper.lambda().orderByDesc(ContractRoomDO::getCreateTime);
                contractList = ContractConvert.INSTANCE.convertList05(contractRoomMapper.selectList(queryWrapper));
                break;
          /*case DELETED:
                //已删除合同(此功能暂时不要)
                contractList = ContractConvert.INSTANCE.convertList04(contractRoomMapper.getDeletedRoomContractByRoomId(roomId));
                break;*/
            default:
                contractList = new ArrayList<>();
                break;
        }
        if (com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isNotEmpty(contractList)) {
            return contractList;
        }
        return null;
    }

    @Override
    public RoomContractRespVO getById(Long id) {
        ContractRoomDO contract = contractRoomMapper.selectById(id);
        RoomContractRespVO roomContractRespVO = ContractConvert.INSTANCE.convert1(contract);
        //备案状态不为空
        if(ObjectUtil.isNotNull(roomContractRespVO.getRecordStatus())){
            //已备案
            RecordStatusEnum.registered.stream().filter(fb->fb.compareTo(roomContractRespVO.getRecordStatus())==0).findFirst().ifPresent(p-> {
                roomContractRespVO.setRecordStatus(ApiConstants.REGISTERED);
            });
            //未备案
            RecordStatusEnum.unRegistered.stream().filter(fb->fb.compareTo(roomContractRespVO.getRecordStatus())==0).findFirst().ifPresent(p-> {
                roomContractRespVO.setRecordStatus(ApiConstants.UN_REGISTERED);
            });
        }
        //通过创建人id获取合同发起人
        if (contract != null) {
            if (contract.getCreator() != null) {
                CommonResult<AdminUserRespDTO> user = adminUserApi.getUser(Long.parseLong(contract.getCreator()));
                if (user.getData() != null) {
                    String nickname = user.getData().getNickname();
                    roomContractRespVO.setOriginatorName(nickname);
                }
            }
            //获取房间租金，用于续租
            CommonResult<RoomHouseRespVo> room = roomHouseApi.getRoomHouseinfoById(contract.getRoomId());
            if (room.getData() != null) {
                if (room.getData().getMonthRental() != null) {
                    roomContractRespVO.setPrice(BigDecimal.valueOf(room.getData().getMonthRental()));
                }
                if (StringUtils.isNotBlank(room.getData().getFullRoomName())) {
                    roomContractRespVO.setFullRoomName(room.getData().getFullRoomName());
                }
            }
        }
        //转换合同状态
        if (roomContractRespVO != null) {
            ContractStatusVO contractStatusVO = transitionStatus(roomContractRespVO.getContractNode(),
                    roomContractRespVO.getContractStatus(), roomContractRespVO.getSignStatus(), roomContractRespVO.getEndTime());
            roomContractRespVO.setContractStatusName(contractStatusVO.getContractStatusName());
            roomContractRespVO.setContractCode(contractStatusVO.getContractCode());
        }
        addCustomerCompany(null, roomContractRespVO);
        return roomContractRespVO;
    }

    @Override
    public List<ContractRoomDO> selectActiveCompanyContract(Long companyContractId) {
        //查找可以退租的子合同(合同是生效、入住中才可以退租且无已生效的退租合同)
        LambdaQueryWrapper<ContractRoomDO> qw = new LambdaQueryWrapper<>();
        qw.eq(Objects.nonNull(companyContractId), ContractRoomDO::getCompanyContractId, companyContractId)
                .in(ContractRoomDO::getContractStatus, Arrays.asList(ContractStatusEnum.ACTIVE.getCode(),
                        ContractStatusEnum.CHECK_IN.getCode()));

        List<ContractRoomDO> contractRoomDOS = contractRoomMapper.selectList(qw);
        //去除有续租已生效的、审批中的合同

        if (CollectionUtils.isNotEmpty(contractRoomDOS)){
            List<Long> ids = contractRoomDOS.stream().map(ContractRoomDO::getId).collect(Collectors.toList());
            LambdaQueryWrapper<ContractRoomDO> qw1 = new LambdaQueryWrapper<>();
            qw1.in(ContractRoomDO::getFromId,ids)
                    .in(ContractRoomDO::getContractStatus,Arrays.asList(ContractStatusEnum.ACTIVE.getCode(),
                            ContractStatusEnum.PENDING.getCode()));
            List<ContractRoomDO> contractRoomDOS1 = contractRoomMapper.selectList(qw1);
            if (CollectionUtils.isNotEmpty(contractRoomDOS1)){
                List<Long> ids1 = contractRoomDOS1.stream().map(ContractRoomDO::getFromId).collect(Collectors.toList());
                return contractRoomDOS.stream().filter(m -> !ids1.contains(m.getId())).collect(Collectors.toList());
            }
        }
        return contractRoomDOS;
    }

    @Override
    public List<CompanyRoomListExcelVO> selectByCustomerCompanyId(Long customerCompanyId) {
        //根据企业用户id查询子合同列表（生效状态）
        LambdaQueryWrapper<ContractRoomDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Objects.nonNull(customerCompanyId), ContractRoomDO::getCustomerCompanyId, customerCompanyId)
                .in(ContractRoomDO::getContractStatus, Arrays.asList(ContractCompanyStatusEnum.ACTIVE.getCode(),ContractStatusEnum.CHECK_IN.getCode()));
        List<ContractRoomDO> contractRoomDOS = contractRoomMapper.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(contractRoomDOS)) {
            Map<Long, ContractRoomDO> contractRoomDOMap = contractRoomDOS.stream()
                    .collect(Collectors.toMap(ContractRoomDO::getRoomId, Function.identity(),(s1, s2) -> s1));
            List<CompanyRoomListExcelVO> companyRoomListExcelVOS = CompanyContractConvert.INSTANCE.convertList4(contractRoomDOS);
            if (CollectionUtils.isNotEmpty(companyRoomListExcelVOS)) {
                for (CompanyRoomListExcelVO companyRoomListExcelVO : companyRoomListExcelVOS) {
                    Long roomId = companyRoomListExcelVO.getRoomId();
                    companyRoomListExcelVO.setFullRoomName(contractRoomDOMap.get(roomId).getRoomName());
                }
            }
            return companyRoomListExcelVOS;
        }
        return null;
    }

    @Override
    public List<ContractRoomDO> getContractByRoomId(Long roomId) {
        return contractRoomMapper.selectList(ContractRoomDO::getRoomId, roomId);
    }

    @Override
    public List<ContractRoomDO> getContractByRoomIds(List<Long> roomIds) {
        return contractRoomMapper.selectList(ContractRoomDO::getRoomId, roomIds);
    }

    /**
     * 入住交房
     */
    @Override
    public void delivery(Long id) {
        ContractRoomDO contractRoomDO = contractRoomMapper.selectById(id);
        if (contractRoomDO == null) {
            throw exception(CONTRACT_NOT_EXISTS);
        }
        List<String> contractStatus = Arrays.asList(ContractStatusEnum.ACTIVE.getCode(),ContractStatusEnum.CHECK_IN.getCode());
        if (!contractStatus.contains(contractRoomDO.getContractStatus())) {
            throw exception(CONTRACT_NO_DELIVERY);
        }
        //设置为  已入住
        contractRoomDO.setContractStatus(ContractStatusEnum.CHECK_IN.getCode());
        contractRoomMapper.updateById(contractRoomDO);
        // todo 设置房态
        updateRoomStatus(contractRoomDO.getRoomId(), contractRoomDO.getContractNode(),
                false, ContractStatusEnum.CHECK_IN.getCode(),
                contractRoomDO.getCustomerName(),contractRoomDO.getCustomerPhone(),contractRoomDO.getId(),contractRoomDO.getEndTime());
        //下发门锁密码
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        if (loginUser == null) {
            throw new ServiceException(ErrorCodeConstants.USER_NOT_LOGIN);
        }
        //企业合同同住人下发密码等到签署安全协议时下发，租客合同同住人不下发密码只下发承住人
        if(CustomerTypeEnum.PERSONAL.getCode().equals(contractRoomDO.getCustomerType())){
            contractRoommateService.issuePassword(contractRoomDO.getRoomId(), contractRoomDO.getCustomerName(), contractRoomDO.getCustomerPhone(),
                    contractRoomDO.getStartTime(), contractRoomDO.getEndTime(), loginUser.getNickname());
        }

        //个人合同才修改入住人入住状态 企业合同需要签署安全协议才改为已入住
        if (contractRoomDO.getCustomerType().equals(CustomerTypeEnum.PERSONAL.getCode())){
            //推送通知
            try {
                log.info("发送新签合同通知，合同：{},消息内容：{}",JSONObject.toJSONString(contractRoomDO),PushServiceEvaluationTypeEnum.NEW.getMsg());
                pushServiceEvaluation(contractRoomDO, PushServiceEvaluationTypeEnum.NEW.getMsg(),
                        PushServiceEvaluationTypeEnum.NEW.getCode());
            } catch (Exception e) {
                log.error("", e);
            }
        }
        contractRoommateMapper.handleRoommateCheckIn(id);
    }

    @Override
    public RentOrderCountVO contractCount(Long id) {
        RentOrderCountVO contractOrderCount = rentOrderService.getContractOrderCount(id);
        return contractOrderCount;
    }

    /**
     * 更新租客基础信息
     */
    @Override
    public void updateTenantInformation(ContractFoundationReqVO reqVO) {
        // 校验存在
        validateExists(reqVO.getId());
        // 更新
        ContractRoomDO updateObj = ContractConvert.INSTANCE.convert(reqVO);
        contractRoomMapper.updateById(updateObj);
    }

    @Override
    public void updateContractInformation(ContractInformationReqVO reqVO) {
        // 校验存在
        validateExists(reqVO.getId());
        // 更新
        ContractRoomDO updateObj = ContractConvert.INSTANCE.convert(reqVO);
        contractRoomMapper.updateById(updateObj);
    }

    @Override
    public List<ContractRoomReqVO> getRoomIdByUserId(Long loginUserId) {
        LambdaQueryWrapper<ContractRoomDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ContractRoomDO::getMemberId, loginUserId)
                .in(ContractRoomDO::getContractStatus, Arrays.asList(ContractStatusEnum.ACTIVE.getCode(),
                        ContractStatusEnum.CHECK_IN.getCode()))
                .orderByDesc(ContractRoomDO::getSignedTime)
        ;
        List<ContractRoomDO> contractRoomDOS = contractRoomMapper.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(contractRoomDOS)) {
            return ContractConvert.INSTANCE.convertList07(contractRoomDOS);
        }
        return null;
    }

    /**
     * node 节点
     * flag 是否审批
     * signStatus 签约状态
     * contractStatus 合同状态
     * 设置房态
     */
    @Override
    public void updateRoomStatus(Long roomId, String node, Boolean needApproval, String contractStatus,String customerName, String customerPhone, Long id, LocalDate endTime) {
        UpdateRoomStateReqDTO updateRoomStateReqDTO = new UpdateRoomStateReqDTO();
        updateRoomStateReqDTO.setRoomHouseId(roomId);
        RoomTenantsInfoDTO roomTenantsInfoDTO = buildRoomTenantsInfoDTO(customerName, customerPhone, id);
        updateRoomStateReqDTO.setRoomTenantsInfo(roomTenantsInfoDTO);
        updateRoomStateReqDTO.setContractInfoDTO(new RoomContractInfoDTO().setExpiryDate(endTime).setContractId(id));
        //退租需要审批
        if (ContractNodeEnum.RENTING_OUT.getCode().equals(node)) {
            if (needApproval) {
                return;
            }
            updateRoomStateReqDTO.setRoomContractStatusEnum(RoomContractStatusEnum.getEnum(contractStatus));
        }
        // 新签 换房 续租 审批驳回
        if (isNewContract(node)) {
            //审批通过
            updateRoomStateReqDTO.setRoomContractStatusEnum(RoomContractStatusEnum.getEnum(contractStatus));
        }
        log.info("修改房态参数：{}", JSONObject.toJSONString(updateRoomStateReqDTO));
        CommonResult<Boolean> commonResult = roomStateRecordApi.updateRoomStateByContract(updateRoomStateReqDTO);
        log.info("创建合同修改房态：{}", commonResult.getData());
        commonResult.checkError();
    }

    private RoomTenantsInfoDTO buildRoomTenantsInfoDTO(String customerName, String customerPhone, Long contractId) {
        RoomTenantsInfoDTO roomTenantsInfoDTO=new RoomTenantsInfoDTO();
        roomTenantsInfoDTO.setName(customerName);
        roomTenantsInfoDTO.setMobile(customerPhone);
        List<LivingWithInfoDTO> livingWithInfoDTOs=new ArrayList<>();
        List<ContractRoommateListReqVO> contractRoommateList = contractRoommateService.getContractRoommateList(contractId);
        if (CollectionUtils.isNotEmpty(contractRoommateList)){
            contractRoommateList = contractRoommateList.stream().filter(f -> "0".equals(f.getRoommateType())).collect(Collectors.toList());
        }
        if (CollectionUtils.isNotEmpty(contractRoommateList)) {
            contractRoommateList.forEach(item->{
                LivingWithInfoDTO livingWithInfoDTO=new LivingWithInfoDTO();
                livingWithInfoDTO.setName(item.getRoommateName());
                livingWithInfoDTO.setMobile(item.getRoommatePhone());
                livingWithInfoDTOs.add(livingWithInfoDTO);
            });
        }
        roomTenantsInfoDTO.setLivingWithInfoDTOs(livingWithInfoDTOs);
        return roomTenantsInfoDTO;
    }

    /*
     * 新签 续租 换房
     * */
    private boolean isNewContract(String node) {
        return ContractNodeEnum.NEW.getCode().equals(node) || ContractNodeEnum.RENEWAL.getCode().equals(node)
                || ContractNodeEnum.EXCHANGE.getCode().equals(node);
    }


    @Override
    public List<ContractTenantInformationRepVO> getRentDataByContractId(Long id) {
        CompanyContractDO companyContractDO = companyContractMapper.selectCompanyContractById(id);
        //判断企业合同状态是够在审批中
        if (companyContractDO.getStatus().equals(ContractStatusEnum.PENDING.getCode())
        || companyContractDO.getStatus().equals(ContractStatusEnum.REJECT.getCode())){
            CommonResult<List<RoomHouseRespVo>> roomHouseByIds = roomHouseApi.getRoomHouseByIds(companyContractDO.getRoomIds());
            List<ContractTenantInformationRepVO> list = new ArrayList<>();
            if (roomHouseByIds.isError()){
                return list;
            }
            //判断是否是统一定价
            if (companyContractDO.getIsUnifiedPricing()){
                for (RoomHouseRespVo datum : roomHouseByIds.getData()) {
                    ContractTenantInformationRepVO repVO = new ContractTenantInformationRepVO();
                    repVO.setFullRoomName(datum.getFullRoomName());
                    repVO.setMonthRent(companyContractDO.getMonthRental());
                    repVO.setRoomId(datum.getId());
                    repVO.setDeposit(companyContractDO.getDeposit());
                    list.add(repVO);
                }
                return list;
            }else {//根据房间定价
                for (RoomHouseRespVo datum : roomHouseByIds.getData()) {
                    ContractTenantInformationRepVO repVO = new ContractTenantInformationRepVO();
                    repVO.setFullRoomName(datum.getFullRoomName());
                    repVO.setMonthRent(BigDecimal.valueOf(datum.getMonthRental()));
                    repVO.setRoomId(datum.getId());
                    repVO.setDeposit(companyContractDO.getDeposit());
                    list.add(repVO);
                }
                return list;
            }
        }else {
            //查出企业所有合同的房间
            List<ContractTenantInformationRepVO> roommateListById = contractRoomMapper.getRoommateListById(id);

            //对结果根据房间分组
            Map<Long, List<ContractTenantInformationRepVO>> collect = roommateListById.stream().collect(Collectors.groupingBy(ContractTenantInformationRepVO::getRoomId));

            List<ContractTenantInformationRepVO> list = new ArrayList<>();

            for (Map.Entry<Long, List<ContractTenantInformationRepVO>> listEntry : collect.entrySet()) {
                //分组中只有一个房间信息直接赋值
                if (listEntry.getValue().size() == 1){
                    list.add(listEntry.getValue().get(0));
                    continue;
                }
                boolean flag = true;
                //找出当前生效的房间
                for (ContractTenantInformationRepVO contractTenantInformationRepVO : listEntry.getValue()) {
                    //如果是已入住的状态，直接赋值
                    if (ContractStatusEnum.CHECK_IN.getCode().equals(contractTenantInformationRepVO.getContractStatus())){
                        list.add(contractTenantInformationRepVO);
                        flag = false;
                        break;
                    }
                    //如果是新签已生效，直接赋值
                    if (ContractStatusEnum.ACTIVE.getCode().equals(contractTenantInformationRepVO.getContractStatus())
                    && (ContractNodeEnum.NEW.getCode().equals(contractTenantInformationRepVO.getContractStatus())
                        && ContractNodeEnum.EXCHANGE.getCode().equals(contractTenantInformationRepVO.getContractStatus()))){
                        list.add(contractTenantInformationRepVO);
                        flag = false;
                        break;
                    }
                    //如果是续租已生效，在周期内直接赋值
                    if (ContractStatusEnum.ACTIVE.getCode().equals(contractTenantInformationRepVO.getContractStatus())
                            && ContractNodeEnum.RENEWAL.getCode().equals(contractTenantInformationRepVO.getContractStatus())
                    && (contractTenantInformationRepVO.getStartTime().isBefore(LocalDate.now())
                        && contractTenantInformationRepVO.getEndTime().isAfter(LocalDate.now()))){
                        list.add(contractTenantInformationRepVO);
                        flag = false;
                        break;
                    }
                }
                //如果没有赋值，说明是退租的.查询就是倒叙的，直接取第一条
                if (flag){
                    list.add(listEntry.getValue().get(0));
                }
            }

            return list;
        }
    }


    @Override
    public ContractRoomListRespVO pageCompanyContractSon(CompanyContractSonPageReqVO pageReqVO) {
        QueryWrapper<ContractRoomDO> qw = getContractRoomDOLambdaQueryWrapper(pageReqVO);
        PageResult<ContractRoomDO> page = contractRoomMapper.selectPage(pageReqVO, qw);

        PageResult<CompanySonsContractListRespVO> pageResult = new PageResult<>();

        ContractRoomListRespVO contractRoomListRespVO = new ContractRoomListRespVO();
        if (CollectionUtils.isNotEmpty(page.getList())) {
            List<CompanySonsContractListRespVO> list = CompanyContractConvert.INSTANCE.convertList(page.getList());
            if (CollectionUtils.isNotEmpty(list)) {
                //字段转义（eg：new-->新签）
                explain(list);
                //计算应收已收金额
                calculatedAmount(list);
                //是否是企业小合同，如果是返回信息加入企业信息
                addCustomerCompany(list, null);
                //备案状态转换
                list.forEach(f->{
                    //备案状态不为空
                    if(ObjectUtil.isNotNull(f.getRecordStatus())){
                        //已备案
                        RecordStatusEnum.registered.stream().filter(fb->fb.compareTo(f.getRecordStatus())==0).findFirst().ifPresent(p-> {
                                    f.setRecordStatus(ApiConstants.REGISTERED);
                                });
                        //未备案
                        RecordStatusEnum.unRegistered.stream().filter(fb->fb.compareTo(f.getRecordStatus())==0).findFirst().ifPresent(p-> {
                                    f.setRecordStatus(ApiConstants.UN_REGISTERED);
                                });
                    }
                });
            }
            pageResult.setList(list);
        }
        //统计新签、续约数量
        statistics(contractRoomListRespVO);
        pageResult.setTotal(page.getTotal());
        contractRoomListRespVO.setContractRoomData(pageResult);
        return contractRoomListRespVO;
    }

    private void addCustomerCompany(List<CompanySonsContractListRespVO> list, RoomContractRespVO contractRespVO) {
        if (CollectionUtils.isNotEmpty(list)) {
            //转换lids
            List<Long> ids = list.stream().map(CompanySonsContractListRespVO::getCustomerCompanyId).filter(Objects::nonNull)
                    .collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(ids)) {
                List<CustomerCompanyDO> customerCompanyList = customerCompanyService.getCustomerCompanyList(ids);
                List<CustomerCompanyRespVO> customerCompanyRespVOS = CustomerCompanyConvert.INSTANCE.convertList(customerCompanyList);
                Map<Long, CustomerCompanyRespVO> collect = customerCompanyRespVOS.stream().collect(Collectors.toMap(CustomerCompanyRespVO::getId,
                        Function.identity(), (oldWorking, newWorking) -> newWorking));
                List<CompanySonsContractListRespVO> collect1 = list.stream().filter(Objects::nonNull)
                        .peek(m -> m.setCustomerCompany(collect.get(m.getCustomerCompanyId()))).collect(Collectors.toList());
            }
        }
        if (contractRespVO != null) {
            CustomerCompanyRespVO customerCompany = customerCompanyService.getCustomerCompany(contractRespVO.getCustomerCompanyId());
            contractRespVO.setCustomerCompany(customerCompany);
        }
    }

    public void statistics(ContractRoomListRespVO contractRoomListRespVO) {
        Long newNum = contractRoomMapper.selectCount(ContractRoomDO::getContractNode, ContractNodeEnum.NEW.getCode());
        Long renewalNum = contractRoomMapper.selectCount(ContractRoomDO::getContractNode, ContractNodeEnum.RENEWAL.getCode());
        Long countNum = contractRoomMapper.selectCount();
        contractRoomListRespVO.setNewNum(newNum.intValue());
        contractRoomListRespVO.setRenewalNum(renewalNum.intValue());
        contractRoomListRespVO.setCountNum(countNum.intValue());
    }

    private void calculatedAmount(List<CompanySonsContractListRespVO> list) {
        if (CollectionUtils.isNotEmpty(list)) {
            for (CompanySonsContractListRespVO listRespVO : list) {
                //得到合同账单列表
                List<RentOrderDO> rentOrderByContractId = rentOrderService.getRentOrderByContractId(listRespVO.getId());
                if (CollectionUtils.isNotEmpty(rentOrderByContractId)) {
                    BigDecimal accumulatedAmountReceivable = BigDecimal.ZERO;
                    BigDecimal accumulatedAmountReceived = BigDecimal.ZERO;
                    //计算应收已收金额
                    for (RentOrderDO rentOrderDO : rentOrderByContractId) {
                        accumulatedAmountReceivable = accumulatedAmountReceivable.add(rentOrderDO.getTotalAmount());
                        accumulatedAmountReceived = accumulatedAmountReceived.add(rentOrderDO.getPaidRentUtilities());
                    }
                    listRespVO.setAccumulatedAmountReceivable(accumulatedAmountReceivable);
                    listRespVO.setAccumulatedAmountReceived(accumulatedAmountReceived);
                }
            }
        }
    }

    @Override
    public List<CompanySonsContractListExcelVO> getContractList(CompanyContractSonPageReqVO pageReqVO) {
        QueryWrapper<ContractRoomDO> qw = getContractRoomDOLambdaQueryWrapper(pageReqVO);
        List<ContractRoomDO> contractRoomDOS = contractRoomMapper.selectList(qw);
        if (CollectionUtils.isNotEmpty(contractRoomDOS)) {
            List<CompanySonsContractListRespVO> list = CompanyContractConvert.INSTANCE.convertList(contractRoomDOS);
            explainExcel(list);
            List<CompanySonsContractListExcelVO> companySonsContractListExcelVOS =
                    CompanyContractConvert.INSTANCE.convertList2(list);
            if(CollectionUtil.isNotEmpty(companySonsContractListExcelVOS)){
                companySonsContractListExcelVOS.forEach(f->{
                    f.setRecordStatusStr("否");
                    //备案状态不为空
                    if(ObjectUtil.isNotNull(f.getRecordStatus())){
                        //已备案
                        RecordStatusEnum.registered.stream().filter(fb->fb.compareTo(f.getRecordStatus())==0).findFirst().ifPresent(p-> {
                            f.setRecordStatusStr("是");
                        });
                        //未备案
                        RecordStatusEnum.unRegistered.stream().filter(fb->fb.compareTo(f.getRecordStatus())==0).findFirst().ifPresent(p-> {
                            f.setRecordStatusStr("否");
                        });
                    }
                });
            }
            return companySonsContractListExcelVOS;
        }
        return null;
    }

    private void explainExcel(List<CompanySonsContractListRespVO> list) {
        if (CollectionUtils.isNotEmpty(list)) {
            //转换lids
            List<Long> ids = list.stream().map(CompanySonsContractListRespVO::getCustomerCompanyId).filter(Objects::nonNull).distinct()
                    .collect(Collectors.toList());
            List<CustomerCompanyDO> customerCompanyList = customerCompanyService.getCustomerCompanyList(ids);
            List<CustomerCompanyRespVO> customerCompanyRespVOS = CustomerCompanyConvert.INSTANCE.convertList(customerCompanyList);
            Map<Long, CustomerCompanyRespVO> collect = customerCompanyRespVOS.stream().collect(Collectors.toMap(CustomerCompanyRespVO::getId,
                    Function.identity(), (oldWorking, newWorking) -> newWorking));

            for (CompanySonsContractListRespVO companySons : list) {
                String contractNode = companySons.getContractNode();
                String contractStatus = companySons.getContractStatus();
                String signStatus = companySons.getSignStatus();
                companySons.setFullRoomName(companySons.getApartmentName()+companySons.getRoomName());
                ContractStatusVO contractStatusVO = transitionStatus(contractNode, contractStatus, signStatus, companySons.getEndTime());
                companySons.setContractCode(contractStatusVO.getContractCode());
                companySons.setContractStatusName(contractStatusVO.getContractStatusName());
                companySons.setContractSort(ContractTypeEnum.getName(companySons.getContractSort()));
                //处理租期
                String date = DateUtils.calculateTimeBetweenStr(companySons.getStartTime(), companySons.getEndTime());
                companySons.setTenancyTerm(date);
                //设置是否是换房
                if (StringUtils.isNotBlank(contractNode)) {
                    if (contractNode.equals(ContractNodeEnum.EXCHANGE.getCode())) {
                        companySons.setIsExchange(ContractIsExchangeType.YES.getDesc());
                    } else {
                        companySons.setIsExchange(ContractIsExchangeType.NO.getDesc());
                    }
                }
                if(Objects.nonNull(companySons.getPayMethodY())){
                    if (companySons.getPayMethodY() != -1){
                        if (Objects.nonNull(companySons.getPayMethodF())) {
                            //付款方式
                            String payMethodStr = toPayMethodStr(companySons.getPayMethodF(), companySons.getPayMethodY());
                            companySons.setPayMethodYStr(payMethodStr);
                        }
                    }else {
                        companySons.setPayMethodYStr("其他");
                    }
                }
                //来源
                if (StringUtils.isNotBlank(contractNode)) {
                    companySons.setContractNode(ContractNodeEnum.getEnum(contractNode).getMsg());
                }
                if (collect.get(companySons.getCustomerCompanyId())!=null) {
                    companySons.setCompanyName(collect.get(companySons.getCustomerCompanyId()).getCompanyName());
                }
            }

        }
    }

    private void explain(List<CompanySonsContractListRespVO> list) {
        if (CollectionUtils.isNotEmpty(list)) {
            //转换lids
            List<Long> ids = list.stream().map(CompanySonsContractListRespVO::getCustomerCompanyId).filter(Objects::nonNull).distinct()
                    .collect(Collectors.toList());
            List<CustomerCompanyDO> customerCompanyList = customerCompanyService.getCustomerCompanyList(ids);
            List<CustomerCompanyRespVO> customerCompanyRespVOS = CustomerCompanyConvert.INSTANCE.convertList(customerCompanyList);
            Map<Long, CustomerCompanyRespVO> collect = customerCompanyRespVOS.stream().collect(Collectors.toMap(CustomerCompanyRespVO::getId,
                    Function.identity(), (oldWorking, newWorking) -> newWorking));
            //如果有企业设置企业名
            list.stream().filter(Objects::nonNull)
                    .forEach(m -> m.setCustomerCompany(collect.get(m.getCustomerCompanyId())));

            //获取房间ids处理房间名称
            List<Long> id1 = list.stream().map(CompanySonsContractListRespVO::getRoomId).collect(Collectors.toList());
            CommonResult<List<RoomHouseRespVo>> roomHouseByIds = roomHouseApi.getRoomHouseByIds(id1);
            Map<Long, RoomHouseRespVo> collect1 = null;
            if (roomHouseByIds.isSuccess()) {
                collect1 = roomHouseByIds.getData().stream().filter(o -> StringUtils.isNotBlank(o.getFullRoomName()))
                        .distinct().collect(Collectors.toMap(RoomHouseRespVo::getId,
                                Function.identity(), (oldWorking, newWorking) -> newWorking));
            }

            for (CompanySonsContractListRespVO companySons : list) {
                String contractNode = companySons.getContractNode();
                String contractStatus = companySons.getContractStatus();
                String signStatus = companySons.getSignStatus();

                ContractStatusVO contractStatusVO = transitionStatus(contractNode, contractStatus, signStatus, companySons.getEndTime());

                companySons.setContractCode(contractStatusVO.getContractCode());
                companySons.setContractStatusName(contractStatusVO.getContractStatusName());

                //处理租期
                String date = DateUtils.calculateTimeBetweenStr(companySons.getStartTime(), companySons.getEndTime());
                companySons.setTenancyTerm(date);
                //设置是否是换房
                if (StringUtils.isNotBlank(contractNode)) {
                    if (contractNode.equals(ContractNodeEnum.EXCHANGE.getCode())) {
                        companySons.setIsExchange(ContractIsExchangeType.YES.getDesc());
                    } else {
                        companySons.setIsExchange(ContractIsExchangeType.NO.getDesc());
                    }
                }
                if(Objects.nonNull(companySons.getPayMethodY())){
                    if (companySons.getPayMethodY() != -1){
                        if (Objects.nonNull(companySons.getPayMethodF())) {
                            //付款方式
                            String payMethodStr = toPayMethodStr(companySons.getPayMethodF(), companySons.getPayMethodY());
                            companySons.setPayMethodYStr(payMethodStr);
                        }
                    }else {
                        companySons.setPayMethodYStr("其他");
                    }
                }
                //房间全称
                if (Objects.nonNull(companySons.getRoomId())) {
//                    CommonResult<RoomHouseRespVo> roomHouseinfoById = roomHouseApi.getRoomHouseinfoById(companySons.getRoomId());
                    if (MapUtils.isNotEmpty(collect1)) {
                        RoomHouseRespVo roomHouseRespVo = collect1.get(companySons.getRoomId());
                        //设置房间全称
                        companySons.setFullRoomName(roomHouseRespVo.getFullRoomName());
                        //设置房间地址
                        String sb = roomHouseRespVo.getProvince() +
                                roomHouseRespVo.getCity() +
                                roomHouseRespVo.getDistrict() +
                                roomHouseRespVo.getStreet() +
                                roomHouseRespVo.getAddress();
                        companySons.setRoomAddress(sb);
                    }
                }
                //来源
                if (StringUtils.isNotBlank(contractNode)) {
                    companySons.setContractNode(ContractNodeEnum.getEnum(contractNode).getMsg());
                }
            }

        }
    }

    private ContractStatusVO transitionStatus(String contractNode, String contractStatus, String signStatus, LocalDate endTime) {
        //合同状态
        String contractStatusName = "";
        String contractCode = "";
        if (isNewContract(contractNode) &&
                ContractStatusEnum.PENDING.getCode().equals(contractStatus)
        ) {
            contractCode = "1";
            contractStatusName = "未签约(合同审批中)";
        }
        if (isNewContract(contractNode) &&
                ContractStatusEnum.NO_SIGNED.getCode().equals(contractStatus) &&
                ContractSignStatusEnum.SIGNED_PARTY_A.getCode().equals(signStatus)) {
            contractCode = "2";
            contractStatusName = "未签约";
        }

        if (isNewContract(contractNode) &&
                ContractStatusEnum.NO_SIGNED.getCode().equals(contractStatus) &&
                ContractSignStatusEnum.SIGNED_PARTY_B.getCode().equals(signStatus)) {
            contractCode = "3";
            contractStatusName = "未签约(乙方待签约)";
        }

        if (ContractNodeEnum.RENTING_OUT.getCode().equals(contractNode) &&
                ContractStatusEnum.PENDING.getCode().equals(contractStatus)) {
            contractCode = "4";
            contractStatusName = "已生效 (退租审批中)";
        }
        if (isNewContract(contractNode) &&
                (ContractStatusEnum.ACTIVE.getCode().equals(contractStatus) ||
                        ContractStatusEnum.CHECK_IN.getCode().equals(contractStatus))
        ) {
            contractCode = "5";
            contractStatusName = "已生效";
        }
        if (isNewContract(contractNode) &&
                ContractStatusEnum.REJECT.getCode().equals(contractStatus)) {
            contractCode = "6";
            contractStatusName = "已驳回";
        }
        if (isNewContract(contractNode) &&
                (ContractStatusEnum.ACTIVE.getCode().equals(contractStatus) ||
                        ContractStatusEnum.CHECK_IN.getCode().equals(contractStatus))
                && endTime.isBefore(LocalDate.now())) {
            contractCode = "7";
            contractStatusName = "已到期";
        }

        if ((ContractNodeEnum.RENTING_OUT.getCode().equals(contractNode) ||
                ContractNodeEnum.RENEWAL.getCode().equals(contractNode))
                && (ContractStatusEnum.NORMAL_END.getCode().equals(contractStatus) ||
                ContractStatusEnum.ABNORMAL_END.getCode().equals(contractStatus))) {
            contractCode = "8";
            contractStatusName = "已退租";
        }
        if (ContractStatusEnum.LOST_EFFECTIVENESS.getCode().equals(contractStatus)) {
            contractCode = "9";
            contractStatusName = "已失效";
        }
        ContractStatusVO contractStatusVO = new ContractStatusVO();
        contractStatusVO.setContractStatusName(contractStatusName);
        contractStatusVO.setContractCode(contractCode);
        return contractStatusVO;
    }


    @NotNull
    private QueryWrapper<ContractRoomDO> getContractRoomDOLambdaQueryWrapper(CompanyContractSonPageReqVO pageReqVO) {
        QueryWrapper<ContractRoomDO> qw = new QueryWrapper<>();
        qw.lambda().eq(Objects.nonNull(pageReqVO.getCompanyContractId()), ContractRoomDO::getCompanyContractId, pageReqVO.getCompanyContractId())
                .eq(Objects.nonNull(pageReqVO.getApartmentId()), ContractRoomDO::getApartmentId, pageReqVO.getApartmentId())
                .eq(Objects.nonNull(pageReqVO.getRoomId()), ContractRoomDO::getSource, pageReqVO.getRoomId())
                .eq(StringUtils.isNotBlank(pageReqVO.getContractSort()), ContractRoomDO::getContractSort, pageReqVO.getContractSort())
                .eq(StringUtils.isNotBlank(pageReqVO.getContractNode()), ContractRoomDO::getContractNode, pageReqVO.getContractNode())
                .eq(Objects.nonNull(pageReqVO.getCustomerCompanyId()), ContractRoomDO::getCustomerCompanyId, pageReqVO.getCustomerCompanyId())
                .eq(Objects.nonNull(pageReqVO.getCustomerType()), ContractRoomDO::getCustomerType, pageReqVO.getCustomerType())
                .like(StringUtils.isNotBlank(pageReqVO.getButlerName()), ContractRoomDO::getButlerName, pageReqVO.getButlerName())
                .in(ObjectUtil.isNotNull(pageReqVO.getRecordStatus()) && ApiConstants.UN_REGISTERED.compareTo(pageReqVO.getRecordStatus()) == 0
                        , ContractRoomDO::getRecordStatus, RecordStatusEnum.unRegistered)
                .in(ObjectUtil.isNotNull(pageReqVO.getRecordStatus()) && ApiConstants.REGISTERED.compareTo(pageReqVO.getRecordStatus()) == 0
                        , ContractRoomDO::getRecordStatus, RecordStatusEnum.registered)
                .between(pageReqVO.getStartStartTime() != null & pageReqVO.getStartEndTime() != null, ContractRoomDO::getStartTime, pageReqVO.getStartStartTime(), pageReqVO.getStartEndTime())
                .between(pageReqVO.getEndStartTime() != null & pageReqVO.getEndEndTime() != null, ContractRoomDO::getEndTime, pageReqVO.getEndStartTime(), pageReqVO.getEndEndTime())
                .between(pageReqVO.getCreateStartTime() != null & pageReqVO.getCreateEndTime() != null, ContractRoomDO::getCreateTime, pageReqVO.getCreateStartTime(), pageReqVO.getCreateEndTime())
                .between(pageReqVO.getUpdateStartTime() != null & pageReqVO.getUpdateEndTime() != null, ContractRoomDO::getUpdateTime, pageReqVO.getUpdateStartTime(), pageReqVO.getUpdateEndTime())
                .orderByDesc(ContractRoomDO::getCreateTime);
        if (StringUtils.isNotBlank(pageReqVO.getNameOrPhone())) {
            qw.like("CONCAT(apartment_name,room_name,customer_phone,customer_name)", pageReqVO.getNameOrPhone());
        }
        // 未签约
        if ("noSigned".equals(pageReqVO.getContractStatus())) {
            qw.lambda().in(ContractRoomDO::getContractNode, Arrays.asList(ContractNodeEnum.NEW.getCode(), ContractNodeEnum.RENEWAL.getCode(), ContractNodeEnum.EXCHANGE.getCode()))
                    .in(ContractRoomDO::getContractStatus, Arrays.asList(ContractStatusEnum.PENDING.getCode(), ContractStatusEnum.NO_SIGNED.getCode()))
            ;
        }
        //已生效
        if ("active".equals(pageReqVO.getContractStatus())) {
            qw.lambda().in(ContractRoomDO::getContractNode, Arrays.asList(ContractNodeEnum.NEW.getCode(), ContractNodeEnum.RENEWAL.getCode(), ContractNodeEnum.EXCHANGE.getCode()))
                    .in(ContractRoomDO::getContractStatus, Arrays.asList(ContractStatusEnum.ACTIVE.getCode(), ContractStatusEnum.CHECK_IN.getCode()))
                    .ge(ContractRoomDO::getEndTime, LocalDate.now())
            ;
        }
        //已驳回
        if ("reject".equals(pageReqVO.getContractStatus())) {
            qw.lambda().in(ContractRoomDO::getContractNode, Arrays.asList(ContractNodeEnum.NEW.getCode(), ContractNodeEnum.RENEWAL.getCode(), ContractNodeEnum.EXCHANGE.getCode()))
                    .eq(ContractRoomDO::getContractStatus, ContractStatusEnum.REJECT.getCode())
            ;
        }
        //已到期
        if ("expired".equals(pageReqVO.getContractStatus())) {
            qw.lambda()
                    .ne(ContractRoomDO::getContractNode, ContractNodeEnum.RENTING_OUT.getCode())
                    .in(ContractRoomDO::getContractStatus, Arrays.asList(ContractStatusEnum.ACTIVE.getCode(), ContractStatusEnum.CHECK_IN.getCode()))
                    .lt(ContractRoomDO::getEndTime, LocalDate.now())
            ;
        }
        //已退租
        if ("rented".equals(pageReqVO.getContractStatus())) {
            qw.lambda().eq(ContractRoomDO::getContractNode, ContractNodeEnum.RENTING_OUT)
                    .in(ContractRoomDO::getContractStatus, Arrays.asList(ContractStatusEnum.NORMAL_END.getCode(), ContractStatusEnum.ABNORMAL_END.getCode()))
            ;
        }
        //已失效
        if (ContractStatusEnum.LOST_EFFECTIVENESS.getCode().equals(pageReqVO.getContractStatus())) {
            qw.lambda()
                    .eq(ContractRoomDO::getContractStatus, ContractStatusEnum.LOST_EFFECTIVENESS.getCode())
            ;
        }
        if (ContractStatusEnum.PENDING.getCode().equals(pageReqVO.getContractStatus())) {
            qw.lambda()
                    .eq(ContractRoomDO::getContractStatus, ContractStatusEnum.PENDING.getCode());
        }

        return qw;
    }

    private String toPayMethodStr(Integer payMethodF, Integer payMethodY) {
        return PAY_STR + payMethodF + DEPOSIT_STR + payMethodY;
    }

    @Override
    public List<ContractRoomRespVO> getContractPhone(String phone, List<String> contractStatus) {
        LambdaQueryWrapper<ContractRoomDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ContractRoomDO::getCustomerPhone, phone)
                .in(CollectionUtils.isNotEmpty(contractStatus), ContractRoomDO::getContractStatus, contractStatus)
        ;
        List<ContractRoomDO> contractRoomDOS = contractRoomMapper.selectList(queryWrapper);

        return ContractConvert.INSTANCE.convertList01(contractRoomDOS);
    }

    @Override
    public PageResult<ContractRoomRespVO> getCustomerContract(PageParam pageParam, Long memberId) {
        log.info("memberId:{}", memberId);
        LambdaQueryWrapper<ContractRoomDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ContractRoomDO::getMemberId, memberId)
                .eq(ContractRoomDO::getCustomerType,CustomerTypeEnum.PERSONAL.getCode())
                .ne(ContractRoomDO::getContractStatus,ContractStatusEnum.REJECT.getCode())
                .orderByDesc(ContractRoomDO::getId);
        PageResult<ContractRoomDO> result = contractRoomMapper.selectPage(pageParam, queryWrapper);
        log.info("result:{}", result.getList().size());
        PageResult<ContractRoomRespVO> resultVo = new PageResult<>();
        List<ContractRoomDO> list = result.getList();
        // 查询入住清单数据
        List<Long> ids = list.stream().map(ContractRoomDO::getId).collect(Collectors.toList());
        List<ContractStayDO> stayList = contractStayService.getStaysByContractIds(ids);
        Map<Long, ContractStayDO> stayMap = null;
        if (CollectionUtils.isNotEmpty(stayList)) {
            stayList = stayList.stream().filter(s -> DeliveryStatusEnum.WAIT_RENTER_CONFIRM.getCode().equals(s.getDeliveryStatus())).collect(Collectors.toList());
            stayMap = stayList.stream().collect(Collectors.toMap(ContractStayDO::getRoomContractId, v -> v));
        }
        if (CollectionUtils.isNotEmpty(list)) {
            List<ContractRoomRespVO> contractRoomRespVOS = ContractConvert.INSTANCE.convertList01(list);
            for (ContractRoomRespVO contractRoomRespVO : contractRoomRespVOS) {
                contractRoomRespVO.setDistanceExpirationTime(ChronoUnit.DAYS.between(contractRoomRespVO.getEndTime(), LocalDate.now()));
                int currentMonthDays = contractRoomRespVO.getEndTime().plusDays(1).getDayOfMonth();
                BigDecimal divide = contractRoomRespVO.getMonthRent().divide(BigDecimal.valueOf(currentMonthDays), 2, RoundingMode.HALF_DOWN);
                contractRoomRespVO.setDailyRent(divide);
                if (Objects.nonNull(stayMap) && stayMap.containsKey(contractRoomRespVO.getId())) {
                    contractRoomRespVO.setStayFlag(true);
                } else {
                    contractRoomRespVO.setStayFlag(false);
                }
            }
            resultVo.setList(contractRoomRespVOS);
        }
        resultVo.setTotal(result.getTotal());
        return resultVo;
    }

    @Override
    public List<ContractRoomDO> getContractByStatus(ContractRoomStatusQueryReqVO param) {
        if (Objects.isNull(param)) {
            return null;
        }
        LambdaQueryWrapper<ContractRoomDO> queryWrapper = new LambdaQueryWrapper<>();
        // 企业客户编号
        queryWrapper.eq(Objects.nonNull(param.getCustomerContractId()), ContractRoomDO::getCustomerCompanyId, param.getCustomerContractId());
        // 合同审批状态
        queryWrapper.eq(StringUtils.isNotBlank(param.getApproveStatus()), ContractRoomDO::getApproveStatus, param.getApproveStatus());
        queryWrapper.in(CollectionUtils.isNotEmpty(param.getApproveStatusList()), ContractRoomDO::getApproveStatus, param.getApproveStatusList());
        queryWrapper.notIn(CollectionUtils.isNotEmpty(param.getApproveStatusWithoutList()), ContractRoomDO::getApproveStatus, param.getApproveStatusWithoutList());
        // 在线签约状态
        queryWrapper.eq(StringUtils.isNotBlank(param.getSignStatus()), ContractRoomDO::getSignStatus, param.getSignStatus());
        queryWrapper.in(CollectionUtils.isNotEmpty(param.getSignStatusList()), ContractRoomDO::getSignStatus, param.getSignStatusList());
        queryWrapper.notIn(CollectionUtils.isNotEmpty(param.getSignStatusWithoutList()), ContractRoomDO::getSignStatus, param.getSignStatusWithoutList());
        // 合同状态
        queryWrapper.eq(StringUtils.isNotBlank(param.getContractStatus()), ContractRoomDO::getContractStatus, param.getContractStatus());
        queryWrapper.in(CollectionUtils.isNotEmpty(param.getContractStatusList()), ContractRoomDO::getContractStatus, param.getContractStatusList());
        queryWrapper.notIn(CollectionUtils.isNotEmpty(param.getContractStatusWithoutList()), ContractRoomDO::getContractStatus, param.getContractStatusWithoutList());
        // 合同节点
        queryWrapper.eq(StringUtils.isNotBlank(param.getContractNode()), ContractRoomDO::getContractNode, param.getContractNode());
        queryWrapper.in(CollectionUtils.isNotEmpty(param.getContractNodeList()), ContractRoomDO::getContractNode, param.getContractNodeList());
        queryWrapper.notIn(CollectionUtils.isNotEmpty(param.getContractNodeWithoutList()), ContractRoomDO::getContractNode, param.getContractNodeWithoutList());
        queryWrapper.like(StringUtils.isNotBlank(param.getRoomName()), ContractRoomDO::getRoomName, param.getRoomName());
        return contractRoomMapper.selectList(queryWrapper);
    }

    @Override
    public void updateStatusAndElectronicNo(ContractUpdateStatusVO req) {
        // 校验存在
        ContractRoomDO contractRoomDO = validateExists(req.getId());
        // 更新
        ContractRoomDO updateObj = ContractConvert.INSTANCE.convert(req);
        contractRoomMapper.updateById(updateObj);
        if (StringUtils.isNotBlank(updateObj.getContractStatus())) {
            if (!ContractNodeEnum.RENEWAL.getCode().equals(contractRoomDO.getContractNode())){
                // 修改房态
                updateRoomStatus(contractRoomDO.getRoomId(), contractRoomDO.getContractNode(), false,
                        StringUtils.isNotBlank(req.getContractStatus()) ? req.getContractStatus() : contractRoomDO.getContractStatus(),
                        contractRoomDO.getCustomerName(),contractRoomDO.getCustomerPhone(),contractRoomDO.getId(),contractRoomDO.getEndTime());
            }

            try {
                // 处理合同预定
                reserveApi.signSuccess(contractRoomDO.getRoomId(), contractRoomDO.getMemberId());
            } catch (Exception e) {
                log.error("电子签约完成，修改预定合同状态，参数roomId：{}，memberId：{}，异常信息：", contractRoomDO.getRoomId(), contractRoomDO.getMemberId());
            }
        }
        contractRoomDO.setContractStatus(req.getContractStatus());
        List<String> nodeList = Arrays.asList(ContractNodeEnum.RENEWAL.getCode(), ContractNodeEnum.EXCHANGE.getCode());
        if (ContractStatusEnum.ACTIVE.getCode().equals(req.getContractStatus()) && nodeList.contains(contractRoomDO.getContractNode())){
            this.dealAutoEffect(contractRoomDO, LocalDate.now());
        }
    }

    @Override
    public PageResult<MemberContractResp> memberContract(MemberContractReq req) {
        Page<MemberContractResp> page = Page.of(req.getPageNo(), req.getPageSize());
        QueryWrapper<MemberContractReq> wrapper = Wrappers.query();
        wrapper.eq("cre.member_id", req.getMemberId())
                .eq("cre.deleted", false)
                .eq("cr.deleted", false);
        contractRoomMapper.memberContractPage(page, wrapper);
        List<MemberContractResp> records = page.getRecords();
        if (CollectionUtils.isNotEmpty(records)) {
            records.forEach(s -> {
                s.setRoommateTypeStr(RoommateTypeEnum.getMsgByCode(s.getRoommateType()));
                s.setStatusStr(RoommateStatusEnum.getMsgByCode(s.getStatus()));
            });
        }
        return new PageResult<>(records, page.getTotal());
    }


    @Override
    public List<ContractRoomDO> getRoomContractListByParam(ContractRoomQueryParamDTO param) {
        if (Objects.isNull(param)) {
            return null;
        }
        LambdaQueryWrapper<ContractRoomDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StringUtils.isNotBlank(param.getContractSort()), ContractRoomDO::getContractSort, param.getContractSort());
        queryWrapper.eq(Objects.nonNull(param.getApartmentId()), ContractRoomDO::getApartmentId, param.getApartmentId());
        queryWrapper.eq(Objects.nonNull(param.getRoomId()), ContractRoomDO::getRoomId, param.getRoomId());
        queryWrapper.eq(StringUtils.isNotBlank(param.getApproveStatus()), ContractRoomDO::getApproveStatus, param.getApproveStatus());
        queryWrapper.eq(StringUtils.isNotBlank(param.getSignStatus()), ContractRoomDO::getSignStatus, param.getSignStatus());
        queryWrapper.eq(StringUtils.isNotBlank(param.getContractStatus()), ContractRoomDO::getContractStatus, param.getContractStatus());
        queryWrapper.in(CollectionUtils.isNotEmpty(param.getContractStatusList()), ContractRoomDO::getContractStatus, param.getContractStatusList());
        queryWrapper.eq(StringUtils.isNotBlank(param.getContractNode()), ContractRoomDO::getContractNode, param.getContractNode());
        queryWrapper.in(CollectionUtils.isNotEmpty(param.getContractNodeList()), ContractRoomDO::getContractNode, param.getContractNodeList());
        queryWrapper.eq(StringUtils.isNotBlank(param.getCustomerType()), ContractRoomDO::getCustomerType, param.getCustomerType());
        queryWrapper.eq(Objects.nonNull(param.getCustomerCompanyId()), ContractRoomDO::getCustomerCompanyId, param.getCustomerCompanyId());
        queryWrapper.eq(Objects.nonNull(param.getCompanyContractId()), ContractRoomDO::getCompanyContractId, param.getCompanyContractId());
        queryWrapper.le(Objects.nonNull(param.getLeChangeEffectiveTime()), ContractRoomDO::getChangeEffectiveTime, param.getLeChangeEffectiveTime());
        queryWrapper.le(Objects.nonNull(param.getLeLessorSignTime()), ContractRoomDO::getLessorSignTime, param.getLeLessorSignTime());
        queryWrapper.eq(Objects.nonNull(param.getTenantId()), ContractRoomDO::getTenantId, param.getTenantId());
        queryWrapper.eq(Objects.nonNull(param.getMemberId()), ContractRoomDO::getMemberId, param.getMemberId());
        queryWrapper.eq(Objects.nonNull(param.getStartTime()), ContractRoomDO::getStartTime, param.getStartTime());
        queryWrapper.lt(Objects.nonNull(param.getLtEndTime()), ContractRoomDO::getEndTime, param.getLtEndTime());
        return contractRoomMapper.selectList(queryWrapper);
    }

    @Override
    @Transactional
    public void updateBatchExpireAndChangeRoomStatus(List<ContractRoomDO> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        List<UpdateRoomStateReqDTO> updateRoomStateReqDTOS = new ArrayList<>();
        for (ContractRoomDO contractRoomDO : list) {
            // 更新合同状态为已失效
            contractRoomDO.setContractStatus(ContractStatusEnum.LOST_EFFECTIVENESS.getCode());
            contractRoomMapper.updateById(contractRoomDO);
            // 删除账单
            rentOrderService.deleteOrderByBusinessId(contractRoomDO.getId(), OrderTypeEnum.DEFAULT.getCode());
            try {
                log.info("请求房源处理合同预定取消，请求参数：roomHouseId:{},memberId:{}", contractRoomDO.getRoomId(), contractRoomDO.getMemberId());
                // 处理合同预定取消
                reserveApi.signCancel(contractRoomDO.getRoomId(), contractRoomDO.getMemberId());
            } catch (Exception e) {
                log.error("修改预定合同状态，参数roomId：{}，memberId：{}，contractStatus:{},异常信息：", contractRoomDO.getRoomId(), contractRoomDO.getMemberId());
            }
            if (ContractNodeEnum.NEW.getCode().equals(contractRoomDO.getContractNode()) || ContractNodeEnum.EXCHANGE.getCode().equals(contractRoomDO.getContractNode())){
                // 更新房态参数
                UpdateRoomStateReqDTO updateRoomStateReqDTO = new UpdateRoomStateReqDTO();
                updateRoomStateReqDTO.setRoomHouseId(contractRoomDO.getRoomId());
                updateRoomStateReqDTO.setRoomContractStatusEnum(RoomContractStatusEnum.TO_BE_CHECK_IN_LOST_EFFECTIVENESS);
                updateRoomStateReqDTO.setRoomTenantsInfo(buildRoomTenantsInfoDTO(contractRoomDO.getCustomerName(),contractRoomDO.getCustomerPhone(),contractRoomDO.getId()));
                updateRoomStateReqDTO.setContractInfoDTO(new RoomContractInfoDTO().setExpiryDate(contractRoomDO.getEndTime()).setContractId(contractRoomDO.getId()));
                updateRoomStateReqDTOS.add(updateRoomStateReqDTO);
            }
        }
        if (CollectionUtils.isNotEmpty(updateRoomStateReqDTOS)){
            try {
                log.info("请求房源批量修改房态，请求参数：{}", JSONObject.toJSONString(updateRoomStateReqDTOS));
                // 批量更新房态
                roomStateRecordApi.updateRoomStateByContract(updateRoomStateReqDTOS);
            } catch (Exception e) {
                log.error("批量修改房态，参数：{}，异常信息", JSONObject.toJSONString(updateRoomStateReqDTOS));
            }
        }

    }

    @Override
    public int countNormalContractByCompanyContractId(Long contractId) {
        return contractRoomMapper.countContractByCompanyContractId(contractId);
    }

    private DistributeLock getLock(Long contractId) {
        return DistributeLock.getLock(String.format("lock:lease:contract:%d", contractId));
    }
    /**
     * 新签用户消息
     */
    @Override
    public void sendNewSignSystemEvent(ContractRoomDO contract) {
        if (!ContractNodeEnum.NEW.getCode().equals(contract.getContractNode())) {
            return;
        }
        // 历史有效合同
        List<ContractRoomDO> list = getHistoryEffectiveContractByMemberId(contract.getMemberId());
        if (CollectionUtils.isNotEmpty(list) && list.size() == 1 && list.get(0).getId().equals(contract.getId())) {
            ContractRoomDO current = list.get(0);
            List<BillPeriod> l = BillPeriod.splitByMonth(contract.getStartTime(), contract.getEndTime(), contract.getPayMethodF());
            if (current.getId().equals(contract.getId())) {
                log.info("新签客户:{}, 合同: {}", contract.getMemberId(), contract.getContractNo());
                LeaseSystemEventNewSign msg = new LeaseSystemEventNewSign();
                msg.setContractId(current.getId());
                msg.setContractNo(current.getContractNo());
                msg.setSignDate(new DateTime(current.getStartTime()).toJdkDate());
                msg.setApartmentId(current.getApartmentId());
                msg.setTenantId(current.getTenantId());
                msg.setRentOrderCount(l.size());
                msg.setUserId(contract.getMemberId());
                msg.setContractStartDate(new DateTime(current.getStartTime()).toJdkDate());
                msg.setContractEndDate(new DateTime(current.getEndTime()).toJdkDate());
                leaseSystemEventProducer.send(msg);
            }
        }
    }

    @Override
    public List<ContractRoomDO> selectListByTimeAndStatus(LocalDateTime monthDate, List<String> statusList) {
        LambdaQueryWrapper<ContractRoomDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(ContractRoomDO::getUpdateTime,monthDate)
                .in(ContractRoomDO::getContractStatus,statusList)
                .eq(ContractRoomDO::getDeleted,0);
        return contractRoomMapper.selectList(wrapper);
    }

    @Override
    public List<ContractRoomDO> getFirstOrderUnpaidContractList(ContractRoomQueryParamDTO param) {
        return contractRoomMapper.getFirstOrderUnpaidContractList(param);
    }

    @Override
    public void cancelSigning(Long id) {
        LambdaQueryWrapper<ContractRoomDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(ContractRoomDO::getId,Arrays.asList(id));
        List<ContractRoomDO> contractRoomDOList = contractRoomMapper.selectList(wrapper);
        rollbackOriginContract(contractRoomDOList);
        updateBatchExpireAndChangeRoomStatus(contractRoomDOList);
    }

    @Override
    public RenterCheckRespVO checkRenterInfo(Long memberNo) {
        if (Objects.isNull(memberNo)){
            return new RenterCheckRespVO(null, false, null,null);
        }

        // 待签约合同
        ContractRoomQueryParamDTO contractParam = new ContractRoomQueryParamDTO();
        contractParam.setMemberId(memberNo);
        contractParam.setSignStatus(ContractSignStatusEnum.SIGNED_PARTY_B.getCode());
        contractParam.setContractStatus(ContractStatusEnum.NO_SIGNED.getCode());
        List<ContractRoomDO> roomContractListByParam = getRoomContractListByParam(contractParam);
        if (CollectionUtils.isNotEmpty(roomContractListByParam)){
            return new RenterCheckRespVO(RenterAppDialogTypeEnum.SIGNING_CONTRACT.getCode(), true, roomContractListByParam.get(0).getId(),roomContractListByParam.get(0).getSignType());
        }

        // 待支付账单
        List<OrderDetaillAndOrderFeeVo> rentOrderList = rentOrderService.getOrder(memberNo, OrderPayStatusEnum.UNPAID.getCode());
        if (CollectionUtils.isNotEmpty(rentOrderList)){
            LocalDate nowDate = LocalDate.now();
            List<OrderDetaillAndOrderFeeVo> firstCollect = rentOrderList.stream().filter(r -> 1 == r.getNum()).collect(Collectors.toList());
            List<OrderDetaillAndOrderFeeVo> collect = rentOrderList.stream().filter(r -> !(nowDate.isBefore(r.getOughtPayTime()))).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(collect) || CollectionUtils.isNotEmpty(firstCollect)){
                return new RenterCheckRespVO(RenterAppDialogTypeEnum.PAYING_ORDER.getCode(), true, null,null);
            }
        }

        // 待签署安全协议
        List<ContractRoommateDO> byParam = contractRoommateService.getRequireSignSafeRoommateList(memberNo);
        if (CollectionUtils.isNotEmpty(byParam)){
            return new RenterCheckRespVO(RenterAppDialogTypeEnum.SIGNING_SECURITY_PROTOCOL.getCode(), true,byParam.get(0).getRoomContractId(),null);
        }

        // 待处理交房
        List<ContractStayDO> stayByMemberNo = contractStayService.getStayByMemberNo(memberNo);
        if (CollectionUtils.isNotEmpty(stayByMemberNo)){
            return new RenterCheckRespVO(RenterAppDialogTypeEnum.PENDING_DELIVERY.getCode(), true,stayByMemberNo.get(0).getRoomContractId(),null);
        }

        // 待签约业主合同
        ContractHouseOwnerQueryDTO ownerQueryDTO = new ContractHouseOwnerQueryDTO();
        ownerQueryDTO.setContractStatus(ContractOwnerStatusEnum.NO_SIGNED.getCode());
        ownerQueryDTO.setApproveStatusList(Arrays.asList(ContractApproveEnum.NO_AUDIT_REQUITED.getCode(),ContractApproveEnum.PASS.getCode()));
        ownerQueryDTO.setSignStatus(ContractSignStatusEnum.SIGNED_PARTY_B.getCode());
        ownerQueryDTO.setContractType(ContractTypeEnum.ELECTRONIC.getCode());
        ownerQueryDTO.setMemberId(memberNo);
        List<ContractHouseOwnerDO> houseOwnerList = contractHouseOwnerService.getByParam(ownerQueryDTO);
        if (CollectionUtils.isNotEmpty(houseOwnerList)){
            return new RenterCheckRespVO(RenterAppDialogTypeEnum.SIGNING_OWNER_CONTRACT.getCode(), true,houseOwnerList.get(0).getId(),null);
        }
        return new RenterCheckRespVO(null, false,null,null);
    }

    @Override
    public void member() {
        LambdaQueryWrapper<ContractRoomDO> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ContractRoomDO::getCustomerType,CustomerTypeEnum.PERSONAL.getCode());
        List<ContractRoomDO> contractRoomDOList = contractRoomMapper.selectList(lambdaQueryWrapper);
        if (CollectionUtils.isNotEmpty(contractRoomDOList)){
            for (ContractRoomDO contractRoomDO : contractRoomDOList) {
                MemberUserReqDTO memberUserReqDTO = new MemberUserReqDTO();
                memberUserReqDTO.setMobile(contractRoomDO.getCustomerPhone());
                memberUserReqDTO.setName(contractRoomDO.getCustomerName());
                memberUserReqDTO.setCompanyId(contractRoomDO.getCustomerCompanyId());
                memberUserReqDTO.setCardNo(contractRoomDO.getCustomerIdNumber());
                //紧急联系人
                EmergencyContact emergencyContact=new EmergencyContact();
                emergencyContact.setName(contractRoomDO.getEmergencyContactName());
                emergencyContact.setMobile(contractRoomDO.getEmergencyContactPhone());
                emergencyContact.setRelationShip(contractRoomDO.getEmergencyContactRelationship());
                List<EmergencyContact> emergencyContacts =new ArrayList<>();
                emergencyContacts.add(emergencyContact);
                memberUserReqDTO.setEmergencyContacts(emergencyContacts);
                if (Objects.nonNull(contractRoomDO.getCustomerEducation())){
                    memberUserReqDTO.setEducation(Integer.valueOf(contractRoomDO.getCustomerEducation()));
                }
                memberUserReqDTO.setAddress(contractRoomDO.getRenterAddress());
                memberUserReqDTO.setEmail(contractRoomDO.getRenterEmail());
                CommonResult<MemberUserRespDTO> userInfo = memberUserApi.getUserInfo(memberUserReqDTO);
                if (userInfo.getCode() != 0) {
                    throw exception(MEMBER_NOT_EXISTS);
                }
            }
        }
    }


    @Override
    public void rollbackOriginContract(List<ContractRoomDO> roomContractList) {
        if (CollectionUtils.isEmpty(roomContractList)){
            return;
        }
        //获取所有同住人
        List<Long> contractIds = roomContractList.stream().map(ContractRoomDO::getId).collect(Collectors.toList());
        List<Long> fromIds = roomContractList.stream().filter(f->f.getFromId()!=null).map(ContractRoomDO::getFromId).collect(Collectors.toList());
        List<ContractRoommateDO> contractRoommateDOS = null;
        if(CollectionUtil.isNotEmpty(fromIds)){
            contractIds.addAll(fromIds);
            contractRoommateDOS =  contractRoommateMapper.selectList(ContractRoommateDO::getRoomContractId, contractIds);
        }else {
            contractRoommateDOS = CollectionUtil.newArrayList();
        }
            //同住人分组
            Map<Long, List<ContractRoommateDO>> collect =
                    contractRoommateDOS.stream().collect(Collectors.groupingBy(ContractRoommateDO::getRoomContractId));
        for (ContractRoomDO contractRoomDO : roomContractList) {
            if(Objects.nonNull(contractRoomDO.getFromId())){
                List<ContractRoommateDO> contractRoommateDO = collect.get(contractRoomDO.getId());
                List<ContractRoommateDO> contractRoommateDO1 = collect.get(contractRoomDO.getFromId());
                //人员回滚 以当前合同主
                if(CollectionUtil.isNotEmpty(contractRoommateDO) && CollectionUtil.isNotEmpty(contractRoommateDO1)){
                    List<ContractRoommateDO> collect1 = contractRoommateDO.stream().map(m -> {
                        //同一个人状态不同
                        Optional<ContractRoommateDO> first =
                                contractRoommateDO1.stream().filter(f -> f.getMemberId().compareTo(m.getMemberId()) == 0
                                        && !f.getStatus().equals(m.getStatus())).findFirst();
                        //存在
                        if (first.isPresent()) {
                            ContractRoommateDO contractRoommateDO2 = first.get();
                            contractRoommateDO2.setStatus(m.getStatus());
                            return contractRoommateDO2;
                        }
                        return null;
                    }).filter(ObjectUtil::isNotNull).collect(Collectors.toList());
                    //回滚
                    if(CollectionUtil.isNotEmpty(collect1)){
                        contractRoommateMapper.updateBatch(collect1,collect1.size());
                    }
                    //原合同人员消失  不进行处理
                }
            }
            if (Objects.nonNull(contractRoomDO.getFromId()) && ContractNodeEnum.RENEWAL.getCode().equals(contractRoomDO.getContractNode())){
                // 续租合同  判断原合同如果为结束，需要回滚原状态
                ContractRoomDO originalContract = this.get(contractRoomDO.getFromId());
                if (Objects.nonNull(originalContract) && ContractStatusEnum.NORMAL_END.getCode().equals(originalContract.getContractStatus())){
                    // 回滚原状态
                    originalContract.setContractStatus(StringUtils.isNotBlank(originalContract.getOldContractStatus())?originalContract.getOldContractStatus():ContractStatusEnum.CHECK_IN.getCode());
                    originalContract.setContractNode(StringUtils.isNotBlank(originalContract.getOldContractNode())?originalContract.getOldContractNode():ContractNodeEnum.NEW.getCode());
                    // 更新状态
                    contractRoomMapper.updateById(originalContract);
                }
            }else if (Objects.nonNull(contractRoomDO.getFromId()) && ContractNodeEnum.EXCHANGE.getCode().equals(contractRoomDO.getContractNode())){
                // 换房合同
                ContractRoomDO originalContract = this.get(contractRoomDO.getFromId());
                if (Objects.nonNull(originalContract) && ContractStatusEnum.FREEZE.getCode().equals(originalContract.getContractStatus())){
                    // 回滚原状态
                    originalContract.setContractStatus(StringUtils.isNotBlank(originalContract.getOldContractStatus())?originalContract.getOldContractStatus():ContractStatusEnum.ACTIVE.getCode());
                    originalContract.setContractNode(StringUtils.isNotBlank(originalContract.getOldContractNode())?originalContract.getOldContractNode():ContractNodeEnum.NEW.getCode());
                    // 更新状态
                    contractRoomMapper.updateById(originalContract);
                }
                // 账单回滚
                LambdaUpdateWrapper<RentOrderDO> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.set(RentOrderDO::getPayStatus, OrderPayStatusEnum.UNPAID.getCode());
                updateWrapper.eq(RentOrderDO::getOrderType, OrderTypeEnum.DEFAULT.getCode());
                updateWrapper.eq(RentOrderDO::getPayStatus, OrderPayStatusEnum.FREEZE.getCode());
                updateWrapper.eq(RentOrderDO::getBusinessId, contractRoomDO.getFromId());
                rentOrderMapper.update(null, updateWrapper);

                // 删除结算单
                LambdaQueryWrapper<SettlementDO> settlementDelWrapper = new LambdaQueryWrapper<>();
                settlementDelWrapper.eq(SettlementDO::getContractId, contractRoomDO.getFromId());
                List<SettlementDO> settlementDOS = settlementMapper.selectList(settlementDelWrapper);
                settlementMapper.delete(settlementDelWrapper);
                // 删除结算单明细
                settlementMapper.deleteDetailsByContractId(contractRoomDO.getFromId());

                SettlementDO settlement = null;
                if (CollectionUtils.isNotEmpty(settlementDOS)){
                    settlement = settlementDOS.get(0);
                    // 结算账单删除
                    LambdaQueryWrapper<RentOrderDO> delWrapper = new LambdaQueryWrapper<>();
                    delWrapper.eq(RentOrderDO::getNum,1);
                    delWrapper.eq(RentOrderDO::getTotalNum,1);
                    delWrapper.eq(RentOrderDO::getBusinessId,contractRoomDO.getFromId());
                    delWrapper.eq(RentOrderDO::getOrderType,OrderTypeEnum.CUSTOM.getCode());
                    delWrapper.eq(RentOrderDO::getStartTime,settlement.getEvictionTime());
                    delWrapper.eq(RentOrderDO::getTotalAmount, settlement.getAmount());
                    List<RentOrderDO> rentOrderDOList = rentOrderMapper.selectList(delWrapper);
                    rentOrderMapper.delete(delWrapper);
                    if (CollectionUtils.isNotEmpty(rentOrderDOList)){
                        // 删除账单明细
                        for (RentOrderDO rentOrderDO : rentOrderDOList) {
                            LambdaQueryWrapper<OrderFeesDO> feesDelWrapper = new LambdaQueryWrapper<>();
                            feesDelWrapper.eq(OrderFeesDO::getOrderId,rentOrderDO.getId());
                            orderFeesMapper.delete(feesDelWrapper);
                        }
                    }
                }
            }
        }
        // 企业合同处理
        if (CustomerTypeEnum.COMPANY.getCode().equals(roomContractList.get(0).getCustomerType())){
            ContractRoomDO companyContractRoom = roomContractList.get(0);
            // 企业续租合同
            if (Objects.nonNull(companyContractRoom.getFromId()) || ContractNodeEnum.RENEWAL.getCode().equals(companyContractRoom.getContractNode())){
                ContractRoomDO contractRoomDO = contractRoomMapper.selectById(companyContractRoom.getFromId());
                if (Objects.nonNull(contractRoomDO)){
                    // 更新原企业大合同
                    LambdaUpdateWrapper<CompanyContractDO> updateWrapper = new LambdaUpdateWrapper<>();
                    updateWrapper.eq(CompanyContractDO::getId, contractRoomDO.getCompanyContractId())
                                    .set(CompanyContractDO::getStatus, ContractCompanyStatusEnum.ACTIVE.getCode());
                    companyContractMapper.update(null,updateWrapper);
                }
            }
        }
    }

    @Override
    public void dealOldContract(Long contractId, String node) {
        ContractRoomDO contractRoomDO = contractRoomMapper.selectById(contractId);
        if (Objects.isNull(contractRoomDO)){
            log.error("换房修改原合同及房态失败，未查询到换房合同，合同号：{}", contractId);
            throw exception(CONTRACT_NOT_EXISTS);
        }
        node = contractRoomDO.getContractNode();
        if (ContractNodeEnum.EXCHANGE.getCode().equals(node)){
            // 换房合同处理
            ContractRoomDO oldContractRoomDO = contractRoomMapper.selectById(contractRoomDO.getFromId());
            if (Objects.isNull(oldContractRoomDO)) {
                log.error("换房修改原合同及房态失败，未查询到换房原合同，合同号：{}", contractId);
                return;
            }
            // 更新合同状态
            oldContractRoomDO.setContractNode(ContractNodeEnum.RENTING_OUT.getCode());
            oldContractRoomDO.setContractStatus(ContractStatusEnum.NORMAL_END.getCode());
            contractRoomMapper.updateById(oldContractRoomDO);

            // 更新账单状态 取消
            LambdaUpdateWrapper<RentOrderDO> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(RentOrderDO::getBusinessId, oldContractRoomDO.getId());
            updateWrapper.eq(RentOrderDO::getOrderType, OrderTypeEnum.DEFAULT.getCode());
            updateWrapper.eq(RentOrderDO::getPayStatus, OrderPayStatusEnum.FREEZE.getCode());
            updateWrapper.set(RentOrderDO::getPayStatus, OrderPayStatusEnum.CANCEL.getCode());
            rentOrderMapper.update(null,updateWrapper);

            // 非迁出的同住人数据
            LambdaQueryWrapper<ContractRoommateDO> qw = new LambdaQueryWrapper<>();
            qw.eq(ContractRoommateDO::getRoomContractId,oldContractRoomDO.getId())
                    .ne(ContractRoommateDO::getStatus,3);
            List<ContractRoommateDO> contractRoommateDOS = contractRoommateMapper.selectList(qw);
            //删除下发密码
            List<String> mobiles = contractRoommateMapper.selectByContractId(oldContractRoomDO.getId());
            ResultModel resultModel = iotRoomDeviceApi.clearPassword1(oldContractRoomDO.getRoomId(),mobiles);
            log.info("删除房间密码:{} resp:{}", oldContractRoomDO.getRoomId(), JSONObject.toJSONString(resultModel));
            if (CollectionUtils.isNotEmpty(contractRoommateDOS)){
                List<Long> collect = contractRoommateDOS.stream().map(ContractRoommateDO::getId).collect(Collectors.toList());
                //迁出同住人
                contractRoommateService.refundRoommateEmigration(collect);
            }

            //修改房态
            updateRoomStatus(oldContractRoomDO.getRoomId(), ContractNodeEnum.RENTING_OUT.getCode(), false, ContractStatusEnum.NORMAL_END.getCode(),
                    oldContractRoomDO.getCustomerName(),oldContractRoomDO.getCustomerPhone(),oldContractRoomDO.getId(),oldContractRoomDO.getEndTime());

            if (CustomerTypeEnum.COMPANY.getCode().equals(oldContractRoomDO.getCustomerType())) {
                //如果是最后一个合同更改大合同合同状态
                companyContractService.setCompanyStatus(oldContractRoomDO.getCompanyContractId(), ContractCompanyStatusEnum.RENTING_OUT.getCode());
            }
            //红冲
            redInvocie(oldContractRoomDO.getEvictionTime(), Collections.singletonList(contractRoomDO.getFromId()));

        }else if (ContractNodeEnum.RENEWAL.getCode().equals(node)){
            // 续租合同处理
            // 调用房源接口
            UpdateContractInfoByRenewalReqVO renewalReqVO = new UpdateContractInfoByRenewalReqVO();
            renewalReqVO.setRenewalContractId(contractRoomDO.getId());
            renewalReqVO.setRoomHouseId(contractRoomDO.getRoomId());
            renewalReqVO.setRenewalEndTime(contractRoomDO.getEndTime());
            roomHouseApi.updateContractInfoByRenewal(renewalReqVO);

            if (CustomerTypeEnum.PERSONAL.getCode().equals(contractRoomDO.getCustomerType())){
                //发送续租短信
                SmsSendSingleToUserReqDTO sms = new SmsSendSingleToUserReqDTO();
                sms.setUserId(contractRoomDO.getMemberId());
                sms.setMobile(contractRoomDO.getCustomerPhone());

                sms.setTemplateCode("contract-sms-relet-success");
                Map<String, Object> templateParams = new HashMap<>();
                if(contractRoomDO.getRoomName().length() > 20){
                    templateParams.put("apartmentName", contractRoomDO.getRoomName().substring(0,20));
                    templateParams.put("roomName", contractRoomDO.getRoomName().substring(21));
                }else {
                    templateParams.put("apartmentName", contractRoomDO.getRoomName());
                    templateParams.put("roomName", "");
                }
                sms.setTemplateParams(templateParams);
                log.info("发送信息参数:{}", JSONObject.toJSONString(sms));
                smsSendApi.sendSingleSmsToMember(sms);
            }
        }
    }

    @Override
    public void dealAutoEffect(ContractRoomDO contractRoomDO, LocalDate time) {
        if (Objects.isNull(contractRoomDO) || Objects.isNull(contractRoomDO.getFromId()) || Objects.isNull(contractRoomDO.getStartTime())){
            return;
        }
        if (contractRoomDO.getStartTime().equals(time) || contractRoomDO.getStartTime().isBefore(time)){
            // 查询对应原合同
            ContractRoomDO originalContract = this.get(contractRoomDO.getFromId());
            if (Objects.nonNull(originalContract) && !ContractStatusEnum.NORMAL_END.getCode().equals(originalContract.getContractStatus())){
                if (ContractNodeEnum.RENEWAL.getCode().equals(contractRoomDO.getContractNode())){
                    // 续租合同
                    // 修改原合同状态正常结束 房态不需要改变
                    ContractUpdateStatusVO updateStatus = new ContractUpdateStatusVO();
                    updateStatus.setId(originalContract.getId());
                    updateStatus.setContractStatus(ContractStatusEnum.NORMAL_END.getCode());
                    updateStatus.setOldContractStatus(originalContract.getContractStatus());
                    updateStatus.setContractNode(ContractNodeEnum.RENTING_OUT.getCode());
                    updateStatus.setOldContractNode(originalContract.getContractNode());
                    this.update(updateStatus);

                    ContractUpdateStatusVO updateContractStatus = new ContractUpdateStatusVO();
                    updateContractStatus.setId(contractRoomDO.getId());
                    updateContractStatus.setMemberId(originalContract.getMemberId());
                    updateContractStatus.setOrderShareMethod(originalContract.getOrderShareMethod());
                    if (ContractStatusEnum.CHECK_IN.getCode().equals(originalContract.getContractStatus())
                            && !ContractStatusEnum.NO_SIGNED.getCode().equals(contractRoomDO.getContractStatus())){
                        updateContractStatus.setContractStatus(ContractStatusEnum.CHECK_IN.getCode());
                    }
                    this.update(updateContractStatus);

                    // 企业子合同续租 如果最后一份子合同续租 企业合同状态改为续租
                    if (CustomerTypeEnum.COMPANY.getCode().equals(contractRoomDO.getCustomerType()) && ContractNodeEnum.RENEWAL.getCode().equals(contractRoomDO.getContractNode())){
                        companyContractService.setCompanyStatus(contractRoomDO.getCompanyContractId(), ContractNodeEnum.RENEWAL.getCode());
                    }
                    // 原合同同住人迁移到当前合同上
                    contractRoommateService.roommateTransfer(originalContract.getId(), contractRoomDO.getId(), contractRoomDO.getRoomId());
                    // 交房记录迁移
                    contractStayService.migrateStayData(Collections.singletonList(contractRoomDO.getId()));
                }else if (ContractNodeEnum.EXCHANGE.getCode().equals(contractRoomDO.getContractNode())){
                    // 换房合同
                    if (ContractStatusEnum.ACTIVE.getCode().equals(contractRoomDO.getContractStatus())){
                        // 首期账单是否支付
                        LambdaQueryWrapper<RentOrderDO> queryWrapper = new LambdaQueryWrapper<>();
                        queryWrapper.eq(RentOrderDO::getBusinessId, contractRoomDO.getId());
                        queryWrapper.eq(RentOrderDO::getOrderType, OrderTypeEnum.DEFAULT.getCode());
                        List<RentOrderDO> rentOrderDOList = rentOrderMapper.selectList(queryWrapper);
                        if (CollectionUtils.isNotEmpty(rentOrderDOList)){
                            RentOrderDO firstRentOrder = rentOrderDOList.stream().filter(r -> 1 == r.getNum()).findFirst().orElse(null);
                            if (Objects.nonNull(firstRentOrder) && OrderPayStatusEnum.PAID.getCode().equals(firstRentOrder.getPayStatus())){
                                // 合同已生效 且首期账单已支付
                                // 释放原合同 房态 账单
                                this.dealOldContract(contractRoomDO.getId(),ContractNodeEnum.EXCHANGE.getCode());
                            }
                        }
                    }
                }
            }
        }
    }

    @Override
    public void updateMemberUser(Long userId, String mobile, String userName, Long oldUserId) {
        contractRoomMapper.updateMemberUser(userId,mobile,userName,oldUserId);
    }


    @Override
    public Boolean checkDisperseRoomEnterContract(List<Long> roomIds,LocalDate ...localDates) {
        log.info("判断分散式房源房间可录入合同，根据房间ID查询房间信息，请求参数：{}", roomIds);
        CommonResult<List<RoomHouseRespVo>> roomHouseByIds = roomHouseApi.getRoomHouseByIds(roomIds);
        log.info("判断分散式房源房间可录入合同，根据房间ID查询房间信息，响应结果：{}", JSONObject.toJSONString(roomHouseByIds));
        roomHouseByIds.checkError();
        List<RoomHouseRespVo> roomDatas = roomHouseByIds.getData();
        Boolean checkFlag = true;
        if (CollectionUtils.isNotEmpty(roomDatas)){
            // 过滤出分散式房源 (此处查到的是房间的roomId，业主合同签约是关联的整套房子的roomId)
            List<Long> disperseRoomIds = roomDatas.stream()
                    .filter(r -> !r.getIsConcentrated())
                    .map(r ->{
                        if (Objects.nonNull(r.getSuperId())){
                            return r.getSuperId();
                        }
                        return r.getId();
                    })
                    .distinct()
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
            log.info("判断分散式房源房间可录入合同，过滤出的分散式房源房间号:{}", JSONObject.toJSONString(disperseRoomIds));
            if (CollectionUtils.isNotEmpty(disperseRoomIds)){
                // 查询业主合同
                List<OwnerContractRoomDTO> ownerOrderByRoomIds = contractHouseOwnerService.getOwnerOrderByRoomIds(disperseRoomIds);
                if (CollectionUtils.isNotEmpty(ownerOrderByRoomIds)){
                    // 无法签约的场景：
                    // 1、有过业主合同，但当前时间无业主合同
                    // 2、当前时间有业主合同，但合同状态非有效
                    Map<Long, List<OwnerContractRoomDTO>> roomOwnerContractMap = ownerOrderByRoomIds.stream().collect(Collectors.groupingBy(OwnerContractRoomDTO::getHouseRoomId));
                    for (Long key : roomOwnerContractMap.keySet()) {
                        //如果一份合同都没有可以直接进行签约
                        List<OwnerContractRoomDTO> ownerContractRoomDTOS = roomOwnerContractMap.get(key);
                        if (CollectionUtils.isEmpty(ownerContractRoomDTOS)){
                            continue;
                        }
                        //过滤出生效和待生效的业主合同
                        List<OwnerContractRoomDTO> collect =
                                ownerContractRoomDTOS.stream().filter(f ->
                                        //合同状态为生效或待生效的
                                 (ContractOwnerStatusEnum.ACTIVE.getCode().equals(f.getContractStatus())
                                || ContractOwnerStatusEnum.WAITING_ACTIVE.getCode().equals(f.getContractStatus()))
                                        //续租结束也算入时间
                                || (ContractOwnerStatusEnum.NORMAL_END.getCode().equals(f.getContractStatus()) &&
                                        !ContractNodeEnum.RENTING_OUT.getCode().equals(f.getContractNode()))).collect(Collectors.toList());
                        //无有效合同 直接不允许签约
                        if(CollectionUtil.isEmpty(collect)){
                            checkFlag = false;
                            break;
                        }
                        LocalDate localDate = LocalDate.now();
                        //获取最大时间结束时间 和最小开始时间
                        LocalDate contractEndTime =
                                collect.stream().max(Comparator.comparing(OwnerContractRoomDTO::getEndTime)).map(OwnerContractRoomDTO::getEndTime).orElseGet(() -> localDate);
                        LocalDate contractStratTime =
                                collect.stream().min(Comparator.comparing(OwnerContractRoomDTO::getStartTime)).map(OwnerContractRoomDTO::getStartTime).orElseGet(() -> localDate);

                        if(localDates!=null && localDates.length > 1 && localDates[0]!=null && localDates[1]!=null){
                            LocalDate startTime = localDates[0];
                            LocalDate endTime = localDates[1];
                            if(contractStratTime.compareTo(startTime) > 0 || contractEndTime.compareTo(endTime) < 0){
                                checkFlag = false;
                                break;
                            }

                        }else{
                            if(contractStratTime.compareTo(localDate) > 0 || contractEndTime.compareTo(localDate) < 0){
                                checkFlag = false;
                                break;
                            }
                        }
                    }
                }
            }
        }
        return checkFlag;
    }


    @Override
    public List<ContractRoomDO> getRoomIdsByMemberId(Long oldUserId) {
        LambdaQueryWrapper<ContractRoomDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ContractRoomDO::getMemberId, oldUserId);
        return contractRoomMapper.selectList(wrapper);
    }

    @Override
    public List<ContractRoomDO> selectOverdue() {
        LambdaQueryWrapper<ContractRoomDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(ContractRoomDO::getContractStatus,Arrays.asList("active", "check_in"));
        queryWrapper.lt(ContractRoomDO::getEndTime,new Date());
        return contractRoomMapper.selectList(queryWrapper);
    }

    @Override
    public ContractRoomDO selectExtensionContract(Long contractId) {
        LambdaQueryWrapper<ContractRoomDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ContractRoomDO::getId,contractId);
        queryWrapper.in(ContractRoomDO::getContractStatus,Arrays.asList("active", "check_in"));
        queryWrapper.lt(ContractRoomDO::getEndTime,new Date());
        return contractRoomMapper.selectOne(queryWrapper);
    }

    @Override
    public Boolean changeHousekeep(ChangeHousekeepReqVO reqVO) {
        LambdaUpdateWrapper<ContractRoomDO> update = Wrappers.lambdaUpdate();
        update.set(ContractRoomDO::getButlerId,reqVO.getButlerId())
                .set(ContractRoomDO::getButlerName,reqVO.getButlerName())
                .in(ContractRoomDO::getId,reqVO.getIds());
        return contractRoomMapper.update(null,update)>0;
    }

    @Override
    public Boolean changeRoomApproval(Long userId, Long storeManager, Long otherStoreManager, String changeType) {
        // 工作流参数
        BpmProcessInstanceCreateReqDTO bpmProcessInstanceCreateReqDTO = new BpmProcessInstanceCreateReqDTO();
        bpmProcessInstanceCreateReqDTO.setProcessDefinitionKey("kmd_sh");
        bpmProcessInstanceCreateReqDTO.setBusinessType(BpmBusinessTypeEnum.CHANGE_CONTRACT_ROOM.name());
        bpmProcessInstanceCreateReqDTO.setTenantId(3953L);
        bpmProcessInstanceCreateReqDTO.setBusinessKey("111111");
        bpmProcessInstanceCreateReqDTO.setServiceName(applicationName);
        log.info("processInstance userId {}, request : {}", userId,
                JsonUtils.toJsonString(bpmProcessInstanceCreateReqDTO));
        HashMap<String, Object> map = MapUtil.of("createName", getLoginUser().getNickname());
        map.put("storeManager",storeManager);
        map.put("otherStoreManager",otherStoreManager);
        map.put("changeType",changeType);
        //写入临时的
        bpmProcessInstanceCreateReqDTO.setVariables(map);
        bpmProcessInstanceCreateReqDTO.setExtendDto(new BpmProcessInstanceCreateReqDTO.ExtendDto());
        CommonResult<String> processInstance =
                bpmProcessInstanceApi.createProcessInstance(userId,
                        bpmProcessInstanceCreateReqDTO);
        log.info("processInstance:{}", JsonUtils.toJsonString(processInstance));
        return true;
    }

    /**
     * 销售报表
     * @param vo
     * @return
     */
    @Override
    public CommonResult<JSONObject> salesStatement(ContractSalesStatementVO vo) {
        JSONObject result = new JSONObject();
        List<ContractRoomDO> contractRoomDOS;
        List<ContractRoomDO> contractRoomDOSAll = contractRoomMapper.queryByHouseTypeAll();
        if(StrUtil.isEmpty(vo.getStartTime()) || StrUtil.isEmpty(vo.getEndTime()))
            contractRoomDOS = contractRoomDOSAll;
        else
            contractRoomDOS = contractRoomMapper.queryByHouseType(vo.getStartTime(), vo.getEndTime());

        List<JSONObject> resultList = new ArrayList<>();
        //查询房源
        CommonResult<List<RoomLayoutVO>> listCommonResult = homeApi.queryRoomLayout();
        List<RoomLayoutVO> roomLayoutVOS = listCommonResult.getData();
        //总面积
        BigDecimal spacesAll = roomLayoutVOS.stream().map(RoomLayoutVO::getSpace).reduce(BigDecimal.ZERO, BigDecimal::add);
        result.put("spacesAll",spacesAll);

        //根据房源id
        Map<Long, List<RoomLayoutVO>> collectByRoomId = roomLayoutVOS.stream().collect(Collectors.groupingBy(RoomLayoutVO::getRoomId, Collectors.toList()));
        //根据户型
        Map<Long, List<RoomLayoutVO>> collectByLayout = roomLayoutVOS.stream().collect(Collectors.groupingBy(RoomLayoutVO::getRoomHouseTypeId, Collectors.toList()));

        List<RoomLayoutVO> roomLayoutVOList = new ArrayList<>();
        List<RoomLayoutVO> roomLayoutVOAllList = new ArrayList<>();
        contractRoomDOSAll.forEach(item->{
            List<RoomLayoutVO> roomLayoutVOS1 = collectByRoomId.get(item.getRoomId());
            if(CollectionUtil.isNotEmpty(roomLayoutVOS1)){
                RoomLayoutVO roomLayoutVO = roomLayoutVOS1.get(0);
                roomLayoutVO.setContractMonthRental(item.getMonthRent());
                roomLayoutVOAllList.add(roomLayoutVO);
            }
        });
        //总出租面积
        BigDecimal spacesLeasedAll = roomLayoutVOAllList.stream().map(RoomLayoutVO::getSpace).reduce(BigDecimal.ZERO, BigDecimal::add);
        result.put("spacesLeasedAll",spacesLeasedAll);
        //整体面积出租率
        BigDecimal areaRentalRateAll = spacesLeasedAll.divide(spacesAll, 2, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100));
        result.put("areaRentalRateAll",areaRentalRateAll+"%");
        //时间中不存在合同房源
        if(CollectionUtil.isEmpty(contractRoomDOS)){
            result.put("spacesLeased","0");
            result.put("areaRentalRate","0%");
            return CommonResult.success(result);
        }

        //查询房源关联
        contractRoomDOS.forEach(item->{
            List<RoomLayoutVO> roomLayoutVOS1 = collectByRoomId.get(item.getRoomId());
            if(CollectionUtil.isNotEmpty(roomLayoutVOS1)){
                RoomLayoutVO roomLayoutVO = roomLayoutVOS1.get(0);
                roomLayoutVO.setContractMonthRental(item.getMonthRent());
                roomLayoutVOList.add(roomLayoutVO);
            }
        });

        //筛选
        if(vo.getRoomHouseTypeId() != null)
            return CommonResult.success(searchRoomType(roomLayoutVOS,roomLayoutVOList,roomLayoutVOAllList,vo,result));

        //已出租面积
        BigDecimal spacesLeased = roomLayoutVOList.stream().map(RoomLayoutVO::getSpace).reduce(BigDecimal.ZERO, BigDecimal::add);
        result.put("spacesLeased",spacesLeased);
        //面积出租率
        BigDecimal areaRentalRate = spacesLeased.divide(spacesAll, 2, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100));
        result.put("areaRentalRate",areaRentalRate+"%");

        Map<Long, List<RoomLayoutVO>> contractByLayout = roomLayoutVOList.stream().collect(Collectors.groupingBy(RoomLayoutVO::getRoomHouseTypeId, Collectors.toList()));

        List<BigDecimal> meanSpacesCollect = new ArrayList<>();
        List<BigDecimal> meanMonthRentalsCollect = new ArrayList<>();
        List<BigDecimal> meanContractMonthRentalsCollect = new ArrayList<>();
        List<BigDecimal> monthAreaCollect = new ArrayList<>();
        List<BigDecimal> dayAreaCollect = new ArrayList<>();
        List<BigDecimal> numCollect = new ArrayList<>();
        List<BigDecimal> spacesCollect = new ArrayList<>();
        List<BigDecimal> contractsSpacesCollect = new ArrayList<>();
        List<BigDecimal> spaceLettingRateCollect = new ArrayList<>();
        List<BigDecimal> priceExecutionRateCollect = new ArrayList<>();

        //统计房型信息
        contractByLayout.forEach((k,v)->{
            //总房源
            List<RoomLayoutVO> layouts = collectByLayout.get(k);
            int size = layouts.size();

            JSONObject jsonObject = new JSONObject();
            jsonObject.put("roomHouseTypeName",v.get(0).getRoomHouseTypeName());

            //平均面积
            BigDecimal spaces = layouts.stream().map(RoomLayoutVO::getSpace).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal meanSpaces = spaces.divide(new BigDecimal(size), 2, BigDecimal.ROUND_HALF_UP);
            jsonObject.put("meanSpaces",meanSpaces);
            meanSpacesCollect.add(meanSpaces);
            //备案均价
            BigDecimal monthRentals = layouts.stream().map(RoomLayoutVO::getMonthRental).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal meanMonthRentals = monthRentals.divide(new BigDecimal(size), 2, BigDecimal.ROUND_HALF_UP);
            jsonObject.put("meanMonthRentals",meanMonthRentals);
            meanMonthRentalsCollect.add(meanMonthRentals);

            //成交均价
            BigDecimal contractMonthRentals = v.stream().map(RoomLayoutVO::getContractMonthRental).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal meanContractMonthRentals = contractMonthRentals.divide(new BigDecimal(v.size()), 2, BigDecimal.ROUND_HALF_UP);
            jsonObject.put("meanContractMonthRentals",meanContractMonthRentals);
            meanContractMonthRentalsCollect.add(meanContractMonthRentals);
            //成交月坪效
            BigDecimal monthArea = contractMonthRentals.divide(spaces,BigDecimal.ROUND_HALF_UP);
            jsonObject.put("monthArea",monthArea);
            monthAreaCollect.add(monthArea);
            //成交日坪效
            BigDecimal dayArea = monthArea.divide(new BigDecimal(30), BigDecimal.ROUND_HALF_UP);
            jsonObject.put("dayArea",dayArea);
            dayAreaCollect.add(dayArea);
            //截止目前出租套数
            jsonObject.put("num",v.size());
            numCollect.add(new BigDecimal(v.size()));
            //已出租面积
            BigDecimal contractsSpaces = v.stream().map(RoomLayoutVO::getSpace).reduce(BigDecimal.ZERO, BigDecimal::add);
            jsonObject.put("contractsSpaces",contractsSpaces);
            contractsSpacesCollect.add(contractsSpaces);
            //总建筑面积
            jsonObject.put("spaces",spaces);
            spacesCollect.add(spaces);
            //面积出租率
            BigDecimal spaceLettingRate = contractsSpaces.divide(spaces,4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)).setScale(2,BigDecimal.ROUND_HALF_UP);
            jsonObject.put("spaceLettingRate",spaceLettingRate+"%");
            spaceLettingRateCollect.add(spaceLettingRate);
            //价格执行率
            List<RoomLayoutVO> boys = v.stream().filter(s->s.getContractMonthRental().compareTo(s.getMonthRental()) == 0).collect(Collectors.toList());
            BigDecimal priceExecutionRate = new BigDecimal(boys.size()).divide(new BigDecimal(v.size()),4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)).setScale(2,BigDecimal.ROUND_HALF_UP);
            jsonObject.put("priceExecutionRate",priceExecutionRate+"%");
            priceExecutionRateCollect.add(priceExecutionRate);
            resultList.add(jsonObject);

        });
        result.put("resultList",resultList);
        //统计汇总
        JSONObject collect = new JSONObject();
        BigDecimal size = BigDecimal.valueOf(contractByLayout.size());
        //平均面积
        BigDecimal meanSpaces = meanSpacesCollect.stream().reduce(BigDecimal.ZERO, BigDecimal::add).divide(size, 2, BigDecimal.ROUND_HALF_UP);
        collect.put("meanSpacesCollect",meanSpaces);
        //备案均价
        BigDecimal meanMonthRentals = meanMonthRentalsCollect.stream().reduce(BigDecimal.ZERO, BigDecimal::add).divide(size, 2, BigDecimal.ROUND_HALF_UP);
        collect.put("meanMonthRentalsCollect",meanMonthRentals);
        //成交均价
        BigDecimal meanContract = meanContractMonthRentalsCollect.stream().reduce(BigDecimal.ZERO, BigDecimal::add).divide(size, 2, BigDecimal.ROUND_HALF_UP);
        collect.put("meanContractMonthRentalsCollect",meanContract);
        //成交日评效
        BigDecimal dayArea = dayAreaCollect.stream().reduce(BigDecimal.ZERO, BigDecimal::add).divide(size,2, BigDecimal.ROUND_HALF_UP);
        collect.put("dayAreaCollect",dayArea);
        //成交月坪效
        BigDecimal monthArea = monthAreaCollect.stream().reduce(BigDecimal.ZERO, BigDecimal::add).divide(size, 2, BigDecimal.ROUND_HALF_UP);
        collect.put("monthAreaCollect",monthArea);
        //截至目前出租套数
        BigDecimal num = numCollect.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
        collect.put("numCollect",num);
        //已出租面积
        BigDecimal spaces = spacesCollect.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
        collect.put("spacesCollect",spaces);
        //总建筑面积
        BigDecimal contractsSpaces = contractsSpacesCollect.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
        collect.put("contractsSpacesCollect",contractsSpaces);
        //面积出租率
        BigDecimal spaceLettingRate = spaceLettingRateCollect.stream().reduce(BigDecimal.ZERO, BigDecimal::add).divide(size, 2, BigDecimal.ROUND_HALF_UP);
        collect.put("spaceLettingRateCollect",spaceLettingRate+"%");
        //价格执行率
        BigDecimal priceExecutionRate = priceExecutionRateCollect.stream().reduce(BigDecimal.ZERO, BigDecimal::add).divide(size, 2, BigDecimal.ROUND_HALF_UP);
        collect.put("priceExecutionRateCollect",priceExecutionRate+"%");
        result.put("collect",collect);
        return CommonResult.success(result);
    }

    /**
     * 筛选户型
     * @param roomLayoutVOS
     * @param roomLayoutVOList
     * @param roomLayoutVOAllList
     * @param vo
     * @param result
     * @return
     */
    private JSONObject searchRoomType(List<RoomLayoutVO> roomLayoutVOS,List<RoomLayoutVO> roomLayoutVOList,List<RoomLayoutVO> roomLayoutVOAllList,ContractSalesStatementVO vo,JSONObject result){
        List<JSONObject> resultList = new ArrayList<>();
        Long roomHouseTypeId = vo.getRoomHouseTypeId();
        //根据房源id
        Map<Long, List<RoomLayoutVO>> collectByRoomId = roomLayoutVOS.stream().collect(Collectors.groupingBy(RoomLayoutVO::getRoomId, Collectors.toList()));
        //根据户型
        Map<Long, List<RoomLayoutVO>> collectByLayout = roomLayoutVOS.stream().collect(Collectors.groupingBy(RoomLayoutVO::getRoomHouseTypeId, Collectors.toList()));

        //总面积
        BigDecimal spacesAll = roomLayoutVOS.stream().map(RoomLayoutVO::getSpace).reduce(BigDecimal.ZERO, BigDecimal::add);
        result.put("spacesAll",spacesAll);

        Map<Long, List<RoomLayoutVO>> contractByLayout = roomLayoutVOList.stream().collect(Collectors.groupingBy(RoomLayoutVO::getRoomHouseTypeId, Collectors.toList()));

        List<RoomLayoutVO> roomLayoutByType = contractByLayout.get(roomHouseTypeId);

        if (CollectionUtil.isEmpty(roomLayoutByType)) {
            result.put("spacesLeased","0");
            result.put("areaRentalRate","0%");
            return result;
        }
        //统计
        //已出租面积
        BigDecimal spacesLeased = roomLayoutVOList.stream().map(RoomLayoutVO::getSpace).reduce(BigDecimal.ZERO, BigDecimal::add);
        result.put("spacesLeased",spacesLeased);
        //面积出租率
        BigDecimal areaRentalRate = spacesLeased.divide(spacesAll, 2, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)).setScale(2,BigDecimal.ROUND_HALF_UP);
        result.put("areaRentalRate",areaRentalRate+"%");

        //总房源
        List<RoomLayoutVO> layouts = collectByLayout.get(roomHouseTypeId);
        int size = layouts.size();

        //统计汇总
        JSONObject collect = new JSONObject();
        //单个房源
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("roomHouseTypeName",roomLayoutByType.get(0).getRoomHouseTypeName());

        //平均面积
        BigDecimal spaces = layouts.stream().map(RoomLayoutVO::getSpace).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal meanSpaces = spaces.divide(new BigDecimal(size), 2, BigDecimal.ROUND_HALF_UP);
        jsonObject.put("meanSpaces",meanSpaces);
        collect.put("meanSpacesCollect",meanSpaces);
        //备案均价
        BigDecimal monthRentals = layouts.stream().map(RoomLayoutVO::getMonthRental).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal meanMonthRentals = monthRentals.divide(new BigDecimal(size), 2, BigDecimal.ROUND_HALF_UP);
        jsonObject.put("meanMonthRentals",meanMonthRentals);
        collect.put("meanMonthRentalsCollect",meanMonthRentals);
        //成交均价
        BigDecimal contractMonthRentals = roomLayoutByType.stream().map(RoomLayoutVO::getContractMonthRental).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal meanContractMonthRentals = contractMonthRentals.divide(new BigDecimal(roomLayoutByType.size()), 2, BigDecimal.ROUND_HALF_UP);
        jsonObject.put("meanContractMonthRentals",meanContractMonthRentals);
        collect.put("meanContractMonthRentalsCollect",meanContractMonthRentals);
        //成交月坪效
        BigDecimal monthArea = contractMonthRentals.divide(spaces,2, BigDecimal.ROUND_HALF_UP);
        jsonObject.put("monthArea",monthArea);
        collect.put("monthAreaCollect",monthArea);
        //成交日坪效
        BigDecimal dayArea = monthArea.divide(new BigDecimal(30), 2, BigDecimal.ROUND_HALF_UP);
        jsonObject.put("dayArea",dayArea);
        collect.put("dayAreaCollect",dayArea);
        //截止目前出租套数
        jsonObject.put("num",roomLayoutByType.size());
        collect.put("numCollect",roomLayoutByType.size());
        //已出租面积
        BigDecimal contractsSpaces = roomLayoutByType.stream().map(RoomLayoutVO::getSpace).reduce(BigDecimal.ZERO, BigDecimal::add);
        jsonObject.put("contractsSpaces",contractsSpaces);
        collect.put("contractsSpacesCollect",contractsSpaces);
        //总建筑面积
        jsonObject.put("spaces",spaces);
        collect.put("spacesCollect",spaces);
        //面积出租率
        BigDecimal spaceLettingRate = contractsSpaces.divide(spaces,2, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)).setScale(2,BigDecimal.ROUND_HALF_UP);
        jsonObject.put("spaceLettingRate",spaceLettingRate+"%");
        collect.put("spaceLettingRateCollect",spaceLettingRate+"%");
        //价格执行率
        List<RoomLayoutVO> boys = roomLayoutByType.stream().filter(s->s.getContractMonthRental().compareTo(s.getMonthRental()) == 0).collect(Collectors.toList());
        BigDecimal priceExecutionRate = new BigDecimal(boys.size()).divide(new BigDecimal(roomLayoutByType.size()), 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)).setScale(2,BigDecimal.ROUND_HALF_UP);
        jsonObject.put("priceExecutionRate",priceExecutionRate+"%");
        collect.put("priceExecutionRateCollect",priceExecutionRate+"%");
        resultList.add(jsonObject);

        result.put("resultList",resultList);
        result.put("collect",collect);
        return result;
    }

}
